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 |
50
.circleci/config.yml
Normal file
50
.circleci/config.yml
Normal file
@@ -0,0 +1,50 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
working_directory: ~/Akarin-project/Akarin
|
||||
parallelism: 1
|
||||
shell: /bin/bash --login
|
||||
environment:
|
||||
CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
|
||||
CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
|
||||
docker:
|
||||
- image: circleci/build-image:ubuntu-14.04-XXL-upstart-1189-5614f37
|
||||
command: /sbin/init
|
||||
steps:
|
||||
# Machine Setup
|
||||
- checkout
|
||||
# Prepare for artifact
|
||||
- run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS
|
||||
- run:
|
||||
working_directory: ~/Akarin-project/Akarin
|
||||
command: sudo update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java; sudo update-alternatives --set javac /usr/lib/jvm/java-8-openjdk-amd64/bin/javac; echo -e "export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64" >> $BASH_ENV
|
||||
# Dependencies
|
||||
# Restore the dependency cache
|
||||
- restore_cache:
|
||||
keys:
|
||||
# This branch if available
|
||||
- v1-dep-{{ .Branch }}-
|
||||
# Default branch if not
|
||||
- v1-dep-ver/1.12.2-
|
||||
# Any branch if there are none on the default branch - this should be unnecessary if you have your default branch configured correctly
|
||||
- v1-dep-
|
||||
- run: git config --global user.email "circle@circleci.com"
|
||||
- run: git config --global user.name "CircleCI"
|
||||
- run: chmod +x scripts/inst.sh
|
||||
- run: ./scripts/inst.sh --setup --remote
|
||||
# Save dependency cache
|
||||
- save_cache:
|
||||
key: v1-dep-{{ .Branch }}-{{ epoch }}
|
||||
paths:
|
||||
- ~/.m2
|
||||
# Test
|
||||
- run: yes|cp -rf ./akarin-*.jar $CIRCLE_ARTIFACTS
|
||||
# Teardown
|
||||
# Save test results
|
||||
- store_test_results:
|
||||
path: /tmp/circleci-test-results
|
||||
# Save artifacts
|
||||
- store_artifacts:
|
||||
path: /tmp/circleci-artifacts
|
||||
- store_artifacts:
|
||||
path: /tmp/circleci-test-results
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +1,4 @@
|
||||
[submodule "work/Paper"]
|
||||
path = work/Paper
|
||||
url = https://github.com/Akarin-project/Paper.git
|
||||
url = https://github.com/Akarin-project/Paper.git
|
||||
branch = pre/1.13
|
||||
17
README.md
17
README.md
@@ -1,8 +1,8 @@
|
||||
# <img src="https://i.loli.net/2018/05/17/5afd869c443ef.png" alt="Akarin Face" align="right">Akarin
|
||||
[](https://akarin.io)
|
||||
[](https://discord.gg/fw2pJAj)
|
||||
[](https://bstats.org/plugin/bukkit/Torch)
|
||||
[](http://ci.ilummc.com/job/Akarin/)
|
||||
[](https://circleci.com/gh/Akarin-project/Akarin/tree/master)
|
||||
[](https://circleci.com/gh/Akarin-project/Akarin/tree/ver/1.13)
|
||||
|
||||
Akarin is currently **under heavy development** and contributions are welcome!
|
||||
|
||||
@@ -24,8 +24,8 @@ Get Akarin
|
||||
---
|
||||
### Download
|
||||
#### Recommended
|
||||
+ [**Jenkins**](http://ci.ilummc.com/job/Akarin/) - Kudos to [Izzel_Aliz](https://github.com/IzzelAliz)
|
||||
+ [**Circle CI**](https://circleci.com/gh/Akarin-project/Akarin/tree/master) - Checkout the 'Artifacts' tab of the latest build
|
||||
+ [**Jenkins**](https://jenkins.bennydoesstuff.me/view/Akarin/) - Kudos to [BennyDoesStuff](https://github.com/BennyDoesTheStuff)
|
||||
+ [**Circle CI**](https://circleci.com/gh/Akarin-project/Akarin/tree/ver/1.13) - Checkout the 'Artifacts' tab of the latest build
|
||||
|
||||
*Open an [Issue](https://github.com/Akarin-project/Akarin/issues) or a [Pull Request](https://github.com/Akarin-project/Akarin/pulls) if you want to add your website here*
|
||||
|
||||
@@ -47,6 +47,7 @@ Get Akarin
|
||||
Demo Servers
|
||||
---
|
||||
* `demo.akarin.io` (official)
|
||||
* `omc.hk` (auth required)
|
||||
|
||||
*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*
|
||||
|
||||
@@ -54,12 +55,6 @@ Contributing
|
||||
---
|
||||
* Akarin uses [Mixin](https://github.com/SpongePowered/Mixin) to modify the code. You can checkout the `sources` folder to see more.
|
||||
* Add your name to the [LICENSE](https://github.com/Akarin-project/Akarin/blob/master/LICENSE.md) if you want to publish your code under the [MIT License](https://github.com/Akarin-project/Akarin/blob/master/licenses/MIT.md).
|
||||
* If you want to join the [Akarin-project](https://github.com/Akarin-project) team, you can send us an email with your experience and necessary information.
|
||||
|
||||
Contact
|
||||
---
|
||||
[Discord](https://discord.gg/D3Rsukh)
|
||||
|
||||
Email: `kira@kira.moe`
|
||||
* If you want to join the [Akarin-project](https://github.com/Akarin-project) team, you can [send](mailto://kira@kira.moe) us an email with your experience and necessary information.
|
||||
|
||||

|
||||
|
||||
16
circle.yml
16
circle.yml
@@ -1,16 +0,0 @@
|
||||
machine:
|
||||
java:
|
||||
version: openjdk8
|
||||
|
||||
dependencies:
|
||||
cache-directories:
|
||||
- "/home/ubuntu/Akarin/work/Paper/work/Minecraft"
|
||||
override:
|
||||
- git config --global user.email "circle@circleci.com"
|
||||
- git config --global user.name "CircleCI"
|
||||
- chmod +x scripts/inst.sh
|
||||
- ./scripts/inst.sh --setup --remote
|
||||
|
||||
test:
|
||||
post:
|
||||
- yes|cp -rf ./akarin-*.jar $CIRCLE_ARTIFACTS
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.DataPalette;
|
||||
import net.minecraft.server.PacketPlayOutMapChunk;
|
||||
|
||||
public class ChunkPacketInfo<T> {
|
||||
|
||||
private final PacketPlayOutMapChunk packetPlayOutMapChunk;
|
||||
private final Chunk chunk;
|
||||
private final int chunkSectionSelector;
|
||||
private ByteBuf data; // Akarin
|
||||
private final int[] bitsPerObject = new int[16];
|
||||
private final Object[] dataPalettes = new Object[16];
|
||||
private final int[] dataBitsIndexes = new int[16];
|
||||
private final Object[][] predefinedObjects = new Object[16][];
|
||||
|
||||
public ChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
|
||||
this.packetPlayOutMapChunk = packetPlayOutMapChunk;
|
||||
this.chunk = chunk;
|
||||
this.chunkSectionSelector = chunkSectionSelector;
|
||||
}
|
||||
|
||||
public PacketPlayOutMapChunk getPacketPlayOutMapChunk() {
|
||||
return packetPlayOutMapChunk;
|
||||
}
|
||||
|
||||
public Chunk getChunk() {
|
||||
return chunk;
|
||||
}
|
||||
|
||||
public int getChunkSectionSelector() {
|
||||
return chunkSectionSelector;
|
||||
}
|
||||
|
||||
public ByteBuf getData() { // Akarin
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(ByteBuf data) { // Akarin
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getBitsPerObject(int chunkSectionIndex) {
|
||||
return bitsPerObject[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setBitsPerObject(int chunkSectionIndex, int bitsPerObject) {
|
||||
this.bitsPerObject[chunkSectionIndex] = bitsPerObject;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public DataPalette<T> getDataPalette(int chunkSectionIndex) {
|
||||
return (DataPalette<T>) dataPalettes[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setDataPalette(int chunkSectionIndex, DataPalette<T> dataPalette) {
|
||||
dataPalettes[chunkSectionIndex] = dataPalette;
|
||||
}
|
||||
|
||||
public int getDataBitsIndex(int chunkSectionIndex) {
|
||||
return dataBitsIndexes[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) {
|
||||
dataBitsIndexes[chunkSectionIndex] = dataBitsIndex;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T[] getPredefinedObjects(int chunkSectionIndex) {
|
||||
return (T[]) predefinedObjects[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setPredefinedObjects(int chunkSectionIndex, T[] predefinedObjects) {
|
||||
this.predefinedObjects[chunkSectionIndex] = predefinedObjects;
|
||||
}
|
||||
|
||||
public boolean isWritten(int chunkSectionIndex) {
|
||||
return bitsPerObject[chunkSectionIndex] != 0;
|
||||
}
|
||||
}
|
||||
62
removed/com/destroystokyo/paper/antixray/DataBitsReader.java
Normal file
62
removed/com/destroystokyo/paper/antixray/DataBitsReader.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class DataBitsReader {
|
||||
|
||||
private ByteBuf dataBits; // Akarin
|
||||
private int bitsPerObject;
|
||||
private int mask;
|
||||
private int longInDataBitsIndex;
|
||||
private int bitInLongIndex;
|
||||
private long current;
|
||||
|
||||
public void setDataBits(ByteBuf dataBits) { // Akarin
|
||||
this.dataBits = dataBits;
|
||||
}
|
||||
|
||||
public void setBitsPerObject(int bitsPerObject) {
|
||||
this.bitsPerObject = bitsPerObject;
|
||||
mask = (1 << bitsPerObject) - 1;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.longInDataBitsIndex = index;
|
||||
bitInLongIndex = 0;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin
|
||||
// Akarin start
|
||||
dataBits.getLong(longInDataBitsIndex);
|
||||
/*
|
||||
current = ((((long) dataBits[longInDataBitsIndex]) << 56)
|
||||
| (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
|
||||
| (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
|
||||
| (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
|
||||
| (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
|
||||
| (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
|
||||
| (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
|
||||
| (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
|
||||
*/ // Akarin end
|
||||
}
|
||||
}
|
||||
|
||||
public int read() {
|
||||
int value = (int) (current >>> bitInLongIndex) & mask;
|
||||
bitInLongIndex += bitsPerObject;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
bitInLongIndex -= 64;
|
||||
longInDataBitsIndex += 8;
|
||||
init();
|
||||
|
||||
if (bitInLongIndex > 0) {
|
||||
value |= current << bitsPerObject - bitInLongIndex & mask;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
94
removed/com/destroystokyo/paper/antixray/DataBitsWriter.java
Normal file
94
removed/com/destroystokyo/paper/antixray/DataBitsWriter.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class DataBitsWriter {
|
||||
|
||||
private ByteBuf dataBits; // Akarin
|
||||
private int bitsPerObject;
|
||||
private long mask;
|
||||
private int longInDataBitsIndex;
|
||||
private int bitInLongIndex;
|
||||
private long current;
|
||||
private boolean dirty;
|
||||
|
||||
public void setDataBits(ByteBuf dataBits) { // Akarin
|
||||
this.dataBits = dataBits;
|
||||
}
|
||||
|
||||
public void setBitsPerObject(int bitsPerObject) {
|
||||
this.bitsPerObject = bitsPerObject;
|
||||
mask = (1 << bitsPerObject) - 1;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.longInDataBitsIndex = index;
|
||||
bitInLongIndex = 0;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin
|
||||
// Akarin start
|
||||
current = dataBits.getLong(longInDataBitsIndex);
|
||||
/*
|
||||
current = ((((long) dataBits[longInDataBitsIndex]) << 56)
|
||||
| (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
|
||||
| (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
|
||||
| (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
|
||||
| (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
|
||||
| (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
|
||||
| (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
|
||||
| (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
|
||||
*/ // Akarin end
|
||||
}
|
||||
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
if (dirty && dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin
|
||||
// Akarin start
|
||||
dataBits.setLong(longInDataBitsIndex, current);
|
||||
/*
|
||||
dataBits[longInDataBitsIndex] = (byte) (current >> 56 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 1] = (byte) (current >> 48 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 2] = (byte) (current >> 40 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 3] = (byte) (current >> 32 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 4] = (byte) (current >> 24 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 5] = (byte) (current >> 16 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 6] = (byte) (current >> 8 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 7] = (byte) (current & 0xff);
|
||||
*/ // Akarin end
|
||||
}
|
||||
}
|
||||
|
||||
public void write(int value) {
|
||||
current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex;
|
||||
dirty = true;
|
||||
bitInLongIndex += bitsPerObject;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
finish();
|
||||
bitInLongIndex -= 64;
|
||||
longInDataBitsIndex += 8;
|
||||
init();
|
||||
|
||||
if (bitInLongIndex > 0) {
|
||||
current = current & ~(mask >>> bitsPerObject - bitInLongIndex) | (value & mask) >>> bitsPerObject - bitInLongIndex;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void skip() {
|
||||
bitInLongIndex += bitsPerObject;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
finish();
|
||||
bitInLongIndex -= 64;
|
||||
longInDataBitsIndex += 8;
|
||||
init();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,15 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.destroystokyo.paper.antixray.ChunkPacketInfo; // Paper - Anti-Xray
|
||||
import com.google.common.collect.Lists;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore.PrivateKeyEntry;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
// Paper start
|
||||
import com.destroystokyo.paper.antixray.PacketPlayOutMapChunkInfo; // Anti-Xray
|
||||
// Paper end
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) WrappedByteBuf -> ByteBuf (compatibility)
|
||||
@@ -21,7 +19,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
||||
private int a;
|
||||
private int b;
|
||||
private int c;
|
||||
private ByteBuf d; // Akarin - byte[] -> ByteBuf
|
||||
private ByteBuf d; private ByteBuf getData() { return this.d; } // Paper - OBFHELPER // Akarin - byte[] -> ByteBuf
|
||||
private List<NBTTagCompound> e;
|
||||
private boolean f;
|
||||
private volatile boolean ready = false; // Paper - Async-Anti-Xray - Ready flag for the network manager
|
||||
@@ -33,21 +31,21 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
||||
// Paper end
|
||||
|
||||
public PacketPlayOutMapChunk(Chunk chunk, int i) {
|
||||
PacketPlayOutMapChunkInfo packetPlayOutMapChunkInfo = chunk.world.chunkPacketBlockController.getPacketPlayOutMapChunkInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info
|
||||
ChunkPacketInfo<IBlockData> chunkPacketInfo = chunk.world.chunkPacketBlockController.getChunkPacketInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info
|
||||
this.a = chunk.locX;
|
||||
this.b = chunk.locZ;
|
||||
this.f = i == '\uffff';
|
||||
boolean flag = chunk.getWorld().worldProvider.m();
|
||||
boolean flag = chunk.getWorld().worldProvider.g();
|
||||
|
||||
this.d = allocateBuffer(this.a(chunk, flag, i)); // Akarin
|
||||
|
||||
// Paper start - Anti-Xray - Add chunk packet info
|
||||
if (packetPlayOutMapChunkInfo != null) {
|
||||
packetPlayOutMapChunkInfo.setData(this.d);
|
||||
if (chunkPacketInfo != null) {
|
||||
chunkPacketInfo.setData(this.getData());
|
||||
}
|
||||
// Paper end
|
||||
|
||||
this.c = this.writeChunk(new PacketDataSerializer(this.d), chunk, flag, i, packetPlayOutMapChunkInfo); // Paper - Anti-Xray - Add chunk packet info // Akarin
|
||||
this.c = this.writeChunk(new PacketDataSerializer(this.getData()), chunk, flag, i, chunkPacketInfo); // Paper - Anti-Xray - Add chunk packet info // Akarin
|
||||
this.e = Lists.newArrayList();
|
||||
Iterator iterator = chunk.getTileEntities().entrySet().iterator();
|
||||
|
||||
@@ -57,14 +55,15 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
||||
TileEntity tileentity = (TileEntity) entry.getValue();
|
||||
int j = blockposition.getY() >> 4;
|
||||
|
||||
if (this.e() || (i & 1 << j) != 0) {
|
||||
NBTTagCompound nbttagcompound = tileentity.d();
|
||||
if (this.f() || (i & 1 << j) != 0) {
|
||||
NBTTagCompound nbttagcompound = tileentity.aa_();
|
||||
if (tileentity instanceof TileEntitySkull) { TileEntitySkull.sanitizeTileEntityUUID(nbttagcompound); } // Paper
|
||||
|
||||
this.e.add(nbttagcompound);
|
||||
}
|
||||
}
|
||||
|
||||
chunk.world.chunkPacketBlockController.modifyBlocks(this, packetPlayOutMapChunkInfo); // Paper - Anti-Xray - Modify blocks
|
||||
chunk.world.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
|
||||
}
|
||||
|
||||
// Paper start - Async-Anti-Xray - Getter and Setter for the ready flag
|
||||
@@ -105,8 +104,8 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
||||
packetdataserializer.writeInt(this.b);
|
||||
packetdataserializer.writeBoolean(this.f);
|
||||
packetdataserializer.d(this.c);
|
||||
packetdataserializer.d(this.d.array().length); // Akarin
|
||||
packetdataserializer.writeBytes(this.d.array());
|
||||
packetdataserializer.d(this.getData().capacity()); // Akarin
|
||||
packetdataserializer.writeBytes(this.getData().array()); // Akarin
|
||||
packetdataserializer.d(this.e.size());
|
||||
Iterator iterator = this.e.iterator();
|
||||
|
||||
@@ -122,33 +121,35 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
||||
packetlistenerplayout.a(this);
|
||||
}
|
||||
|
||||
private ByteBuf allocateBuffer(int expectedCapacity) { return g(expectedCapacity); } // Akarin - OBFHELPER
|
||||
private ByteBuf g(int expectedCapacity) { // Akarin - added argument
|
||||
ByteBuf bytebuf = Unpooled.buffer(expectedCapacity); // Akarin
|
||||
private ByteBuf h() { return allocateBuffer(-1); } // Akarin
|
||||
private ByteBuf allocateBuffer(int expectedCapacity) { // Akarin - added argument
|
||||
ByteBuf bytebuf = expectedCapacity == -1 ? Unpooled.buffer() : Unpooled.buffer(expectedCapacity); // Akarin
|
||||
|
||||
bytebuf.writerIndex(0);
|
||||
return bytebuf;
|
||||
}
|
||||
|
||||
// Paper start - Anti-Xray - Support default method
|
||||
public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector); } // OBFHELPER
|
||||
// Paper start - Anti-Xray - Support default methods
|
||||
public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector); }
|
||||
public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i) {
|
||||
return this.a(packetdataserializer, chunk, flag, i, null);
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector, PacketPlayOutMapChunkInfo packetPlayOutMapChunkInfo) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector, packetPlayOutMapChunkInfo); } // Paper - Anti-Xray - OBFHELPER
|
||||
public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i, PacketPlayOutMapChunkInfo packetPlayOutMapChunkInfo) { // Paper - Anti-Xray - Add chunk packet info
|
||||
public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector, ChunkPacketInfo<IBlockData> chunkPacketInfo) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector, chunkPacketInfo); } // Paper - OBFHELPER // Paper - Anti-Xray - Add chunk packet info
|
||||
public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i, ChunkPacketInfo<IBlockData> chunkPacketInfo) { // Paper - Anti-Xray - Add chunk packet info
|
||||
int j = 0;
|
||||
ChunkSection[] achunksection = chunk.getSections();
|
||||
int k = 0;
|
||||
|
||||
for (int l = achunksection.length; k < l; ++k) {
|
||||
int l;
|
||||
|
||||
for (l = achunksection.length; k < l; ++k) {
|
||||
ChunkSection chunksection = achunksection[k];
|
||||
|
||||
if (chunksection != Chunk.a && (!this.e() || !chunksection.a()) && (i & 1 << k) != 0) {
|
||||
if (chunksection != Chunk.a && (!this.f() || !chunksection.a()) && (i & 1 << k) != 0) {
|
||||
j |= 1 << k;
|
||||
chunksection.getBlocks().writeBlocks(packetdataserializer, packetPlayOutMapChunkInfo, k); // Paper - Anti-Xray - Add chunk packet info
|
||||
chunksection.getBlocks().writeDataPaletteBlock(packetdataserializer, chunkPacketInfo, k); // Paper - Anti-Xray - Add chunk packet info
|
||||
packetdataserializer.writeBytes(chunksection.getEmittedLightArray().asBytes());
|
||||
if (flag) {
|
||||
packetdataserializer.writeBytes(chunksection.getSkyLightArray().asBytes());
|
||||
@@ -156,8 +157,12 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.e()) {
|
||||
packetdataserializer.writeBytes(chunk.getBiomeIndex());
|
||||
if (this.f()) {
|
||||
BiomeBase[] abiomebase = chunk.getBiomeIndex();
|
||||
|
||||
for (l = 0; l < abiomebase.length; ++l) {
|
||||
packetdataserializer.writeInt(IRegistry.BIOME.a(abiomebase[l])); // Paper - decompile fix
|
||||
}
|
||||
}
|
||||
|
||||
return j;
|
||||
@@ -171,7 +176,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
||||
for (int l = achunksection.length; k < l; ++k) {
|
||||
ChunkSection chunksection = achunksection[k];
|
||||
|
||||
if (chunksection != Chunk.a && (!this.e() || !chunksection.a()) && (i & 1 << k) != 0) {
|
||||
if (chunksection != Chunk.a && (!this.f() || !chunksection.a()) && (i & 1 << k) != 0) {
|
||||
j += chunksection.getBlocks().a();
|
||||
j += chunksection.getEmittedLightArray().asBytes().length;
|
||||
if (flag) {
|
||||
@@ -180,14 +185,14 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.e()) {
|
||||
j += chunk.getBiomeIndex().length;
|
||||
if (this.f()) {
|
||||
j += chunk.getBiomeIndex().length * 4;
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
public boolean e() {
|
||||
public boolean f() {
|
||||
return this.f;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
set -e
|
||||
basedir="$(cd "$1" && pwd -P)"
|
||||
workdir="$basedir/work"
|
||||
version="ver/1.13"
|
||||
paperbasedir="$basedir/work/Paper"
|
||||
paperworkdir="$basedir/work/Paper/work"
|
||||
|
||||
@@ -16,6 +17,7 @@ if [ "$2" == "--setup" ] || [ "$3" == "--setup" ] || [ "$4" == "--setup" ]; then
|
||||
rm Minecraft/ -r
|
||||
fi
|
||||
git clone https://github.com/Akarin-project/Minecraft.git
|
||||
cd "Minecraft" && git checkout "$version"
|
||||
fi
|
||||
|
||||
cd "$paperbasedir"
|
||||
@@ -25,6 +27,8 @@ fi
|
||||
|
||||
echo "[Akarin] Ready to build"
|
||||
(
|
||||
cd "$paperworkdir/BuildData" && git checkout "$version"
|
||||
cd "$paperbasedir"
|
||||
echo "[Akarin] Touch sources.."
|
||||
|
||||
cd "$paperbasedir"
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
(
|
||||
set -e
|
||||
basedir="$pwd"
|
||||
basedir="$(pwd -P)"
|
||||
version="pre/1.13"
|
||||
|
||||
(git submodule update --init --remote && chmod +x scripts/build.sh && ./scripts/build.sh "$basedir" "$1" "$2" "$3") || (
|
||||
(git submodule update --init --remote && cd "work/Paper" && git checkout "$version" && cd "$basedir" && chmod +x scripts/build.sh && ./scripts/build.sh "$basedir" "$1" "$2" "$3") || (
|
||||
echo "Failed to build Akarin"
|
||||
exit 1
|
||||
) || exit 1
|
||||
|
||||
102
sources/pom.xml
102
sources/pom.xml
@@ -3,22 +3,22 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>akarin</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.12.2-R0.4-RELEASE</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>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
|
||||
<parent>
|
||||
<groupId>com.destroystokyo.paper</groupId>
|
||||
<artifactId>paper-parent</artifactId>
|
||||
@@ -26,13 +26,6 @@
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<!-- bugfixes (netty#6607) -->
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.1.24.Final</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.destroystokyo.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
@@ -46,30 +39,11 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.jopt-simple</groupId>
|
||||
<artifactId>jopt-simple</artifactId>
|
||||
<version>5.0.4</version>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>minecraft-server</artifactId>
|
||||
<version>${minecraft.version}-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.21.0.1</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.45</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.trove4j</groupId>
|
||||
<artifactId>trove4j</artifactId>
|
||||
<version>3.0.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.minecrell</groupId>
|
||||
<artifactId>terminalconsoleappender</artifactId>
|
||||
@@ -81,7 +55,6 @@
|
||||
<version>4.5.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
Required to add the missing Log4j2Plugins.dat file from log4j-core
|
||||
which has been removed by Mojang. Without it, log4j has to classload
|
||||
@@ -94,8 +67,6 @@
|
||||
<version>2.8.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Paper - Add additional Log4J dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
@@ -107,7 +78,6 @@
|
||||
<artifactId>log4j-iostreams</artifactId>
|
||||
<version>2.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Paper - Async loggers -->
|
||||
<dependency>
|
||||
<groupId>com.lmax</groupId>
|
||||
@@ -115,7 +85,30 @@
|
||||
<version>3.4.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
<version>6.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.23.1</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.46</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.trove4j</groupId>
|
||||
<artifactId>trove4j</artifactId>
|
||||
<version>3.0.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- testing -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
@@ -129,12 +122,11 @@
|
||||
<version>1.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Akarin -->
|
||||
<dependency>
|
||||
<groupId>io.akarin</groupId>
|
||||
<artifactId>legacylauncher</artifactId>
|
||||
<version>1.25</version>
|
||||
<version>2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
@@ -190,7 +182,7 @@
|
||||
<version>1.3</version>
|
||||
<configuration>
|
||||
<outputPrefix>git-Akarin-</outputPrefix>
|
||||
<scmDirectory>../..</scmDirectory> <!-- Akarin -->
|
||||
<scmDirectory>../..</scmDirectory>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
@@ -211,6 +203,7 @@
|
||||
<manifestEntries>
|
||||
<Main-Class>net.minecraft.launchwrapper.Launch</Main-Class>
|
||||
<Implementation-Title>CraftBukkit</Implementation-Title>
|
||||
<!--suppress MavenModelInspection -->
|
||||
<Implementation-Version>${describe}</Implementation-Version>
|
||||
<Implementation-Vendor>${maven.build.timestamp}</Implementation-Vendor>
|
||||
<Specification-Title>Bukkit</Specification-Title>
|
||||
@@ -243,7 +236,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.1.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
@@ -287,15 +280,15 @@
|
||||
</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>
|
||||
<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>
|
||||
@@ -315,8 +308,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<!-- BUILDTOOLS-362 / SUREFIRE-1444 - newer version fails in Docker -->
|
||||
<version>2.20</version>
|
||||
<version>2.12.4</version>
|
||||
<configuration>
|
||||
<workingDirectory>${basedir}/target/test-server</workingDirectory>
|
||||
<excludes>
|
||||
@@ -340,7 +332,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>animal-sniffer-maven-plugin</artifactId>
|
||||
<version>1.16</version>
|
||||
<version>1.17</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>process-classes</phase>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -24,19 +24,22 @@
|
||||
package co.aikar.timings;
|
||||
|
||||
import co.aikar.util.LoadingIntMap;
|
||||
import io.akarin.api.internal.Akari;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Add volatile to fields (safety issue)
|
||||
* 1) Thread safe timing (safety)
|
||||
*/
|
||||
class TimingHandler implements Timing {
|
||||
|
||||
private static int idPool = 1;
|
||||
final int id = idPool++;
|
||||
private static AtomicInteger idPool = new AtomicInteger(1);
|
||||
final int id = idPool.getAndIncrement();
|
||||
|
||||
final String name;
|
||||
private final boolean verbose;
|
||||
@@ -46,12 +49,12 @@ class TimingHandler implements Timing {
|
||||
final TimingData record;
|
||||
private final TimingHandler groupHandler;
|
||||
|
||||
private volatile long start = 0; // Akarin - volatile
|
||||
private volatile int timingDepth = 0; // Akarin - volatile
|
||||
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 TimingHandler parent;
|
||||
private volatile TimingHandler parent; // Akarin
|
||||
|
||||
TimingHandler(TimingIdentifier id) {
|
||||
if (id.name.startsWith("##")) {
|
||||
@@ -74,61 +77,51 @@ 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() {
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
startTiming();
|
||||
}
|
||||
startTiming();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopTimingIfSync() {
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
stopTiming();
|
||||
}
|
||||
stopTiming();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timing startTiming() {
|
||||
if (enabled && ++timingDepth == 1) {
|
||||
start = System.nanoTime();
|
||||
if (enabled && /*Bukkit.isPrimaryThread() &&*/ timingDepth.incrementAndGet() == 1) { // Akarin
|
||||
start.getAndSet(System.nanoTime());
|
||||
parent = TimingsManager.CURRENT;
|
||||
TimingsManager.CURRENT = this;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopTiming() {
|
||||
if (enabled && --timingDepth == 0 && start != 0) {
|
||||
if (!Bukkit.isPrimaryThread()) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name);
|
||||
new Throwable().printStackTrace();
|
||||
start = 0;
|
||||
return;
|
||||
}
|
||||
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 void abort() {
|
||||
if (enabled && timingDepth > 0) {
|
||||
start = 0;
|
||||
if (enabled && timingDepth.get() > 0) {
|
||||
start.getAndSet(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,18 +129,24 @@ class TimingHandler implements Timing {
|
||||
if (TimingsManager.CURRENT == this) {
|
||||
TimingsManager.CURRENT = parent;
|
||||
if (parent != null) {
|
||||
Akari.timingsLock.lock(); // Akarin
|
||||
parent.children.get(id).add(diff);
|
||||
Akari.timingsLock.unlock(); // Akarin
|
||||
}
|
||||
}
|
||||
record.add(diff);
|
||||
if (!added) {
|
||||
added = true;
|
||||
timed = true;
|
||||
Akari.timingsLock.lock(); // Akarin
|
||||
TimingsManager.HANDLERS.add(this);
|
||||
Akari.timingsLock.unlock(); // Akarin
|
||||
}
|
||||
if (groupHandler != null) {
|
||||
groupHandler.addDiff(diff);
|
||||
Akari.timingsLock.lock(); // Akarin
|
||||
groupHandler.children.get(id).add(diff);
|
||||
Akari.timingsLock.unlock(); // Akarin
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,10 +160,12 @@ class TimingHandler implements Timing {
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -205,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,85 +0,0 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.DataPalette;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.PacketPlayOutMapChunk;
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) byte[] -> ByteBuf (compatibility)
|
||||
*/
|
||||
public class PacketPlayOutMapChunkInfo {
|
||||
|
||||
private final PacketPlayOutMapChunk packetPlayOutMapChunk;
|
||||
private final Chunk chunk;
|
||||
private final int chunkSectionSelector;
|
||||
private ByteBuf data; // Akarin - byte[] -> ByteBuf
|
||||
private final int[] bitsPerValue = new int[16];
|
||||
private final DataPalette[] dataPalettes = new DataPalette[16];
|
||||
private final int[] dataBitsIndexes = new int[16];
|
||||
private final IBlockData[][] predefinedBlockData = new IBlockData[16][];
|
||||
|
||||
public PacketPlayOutMapChunkInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
|
||||
this.packetPlayOutMapChunk = packetPlayOutMapChunk;
|
||||
this.chunk = chunk;
|
||||
this.chunkSectionSelector = chunkSectionSelector;
|
||||
}
|
||||
|
||||
public PacketPlayOutMapChunk getPacketPlayOutMapChunk() {
|
||||
return packetPlayOutMapChunk;
|
||||
}
|
||||
|
||||
public Chunk getChunk() {
|
||||
return chunk;
|
||||
}
|
||||
|
||||
public int getChunkSectionSelector() {
|
||||
return chunkSectionSelector;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data.array(); // Akarin
|
||||
}
|
||||
|
||||
public void setData(ByteBuf data) { // Akarin - byte[] -> ByteBuf
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getBitsPerValue(int chunkSectionIndex) {
|
||||
return bitsPerValue[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setBitsPerValue(int chunkSectionIndex, int bitsPerValue) {
|
||||
this.bitsPerValue[chunkSectionIndex] = bitsPerValue;
|
||||
}
|
||||
|
||||
public DataPalette getDataPalette(int chunkSectionIndex) {
|
||||
return dataPalettes[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setDataPalette(int chunkSectionIndex, DataPalette dataPalette) {
|
||||
dataPalettes[chunkSectionIndex] = dataPalette;
|
||||
}
|
||||
|
||||
public int getDataBitsIndex(int chunkSectionIndex) {
|
||||
return dataBitsIndexes[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) {
|
||||
dataBitsIndexes[chunkSectionIndex] = dataBitsIndex;
|
||||
}
|
||||
|
||||
public IBlockData[] getPredefinedBlockData(int chunkSectionIndex) {
|
||||
return predefinedBlockData[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setPredefinedBlockData(int chunkSectionIndex, IBlockData[] predefinedBlockData) {
|
||||
this.predefinedBlockData[chunkSectionIndex] = predefinedBlockData;
|
||||
}
|
||||
|
||||
public boolean isWritten(int chunkSectionIndex) {
|
||||
return bitsPerValue[chunkSectionIndex] != 0;
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,10 @@ 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.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import com.google.common.collect.Queues;
|
||||
@@ -14,6 +14,9 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import co.aikar.timings.Timing;
|
||||
import co.aikar.timings.Timings;
|
||||
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;
|
||||
|
||||
@@ -44,19 +47,49 @@ public abstract class Akari {
|
||||
}
|
||||
|
||||
public static class AssignableFactory implements ThreadFactory {
|
||||
private final String threadName;
|
||||
private int threadNumber;
|
||||
|
||||
public AssignableFactory(String name) {
|
||||
threadName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable run) {
|
||||
Thread thread = new AssignableThread(run);
|
||||
thread.setName("Akarin Parallel Schedule Thread");
|
||||
thread.setName(StringUtils.replaceChars(threadName, "$", String.valueOf(threadNumber++)));
|
||||
thread.setPriority(AkarinGlobalConfig.primaryThreadPriority); // Fair
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A common tick pool
|
||||
*/
|
||||
public static final ExecutorCompletionService<?> STAGE_TICK = new ExecutorCompletionService<>(Executors.newSingleThreadExecutor(new AssignableFactory()));
|
||||
public static SuspendableExecutorCompletionService<?> STAGE_TICK;
|
||||
|
||||
static {
|
||||
resizeTickExecutors(3);
|
||||
}
|
||||
|
||||
public static void resizeTickExecutors(int worlds) {
|
||||
int parallelism;
|
||||
switch (AkarinGlobalConfig.parallelMode) {
|
||||
case -1:
|
||||
return;
|
||||
case 0:
|
||||
parallelism = 2;
|
||||
break;
|
||||
case 1:
|
||||
parallelism = worlds + 1;
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
parallelism = worlds * 2;
|
||||
break;
|
||||
}
|
||||
STAGE_TICK = new SuspendableExecutorCompletionService<>(new SuspendableThreadPoolExecutor(parallelism, parallelism,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<Runnable>(),
|
||||
new AssignableFactory("Akarin Parallel Ticking Thread - $")));
|
||||
}
|
||||
|
||||
public static boolean isPrimaryThread() {
|
||||
return isPrimaryThread(true);
|
||||
@@ -64,7 +97,7 @@ public abstract class Akari {
|
||||
|
||||
public static boolean isPrimaryThread(boolean assign) {
|
||||
Thread current = Thread.currentThread();
|
||||
return current == MinecraftServer.getServer().primaryThread || (assign ? current instanceof AssignableThread : false);
|
||||
return current == MinecraftServer.getServer().primaryThread || (assign ? (current.getClass() == AssignableThread.class) : false);
|
||||
}
|
||||
|
||||
public static final String EMPTY_STRING = "";
|
||||
@@ -91,14 +124,17 @@ 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 entityCallbackTiming = getTiming("Akarin - Entity Callback");
|
||||
|
||||
public final static Timing callbackTiming = getTiming("Akarin - Callback");
|
||||
public final static Timing callbackTiming = getTiming("Akarin - Callback Queue");
|
||||
|
||||
private static Timing getTiming(String name) {
|
||||
try {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package io.akarin.api.internal.mixin;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.EnumSkyBlock;
|
||||
|
||||
public interface IMixinChunk {
|
||||
AtomicInteger getPendingLightUpdates();
|
||||
|
||||
long getLightUpdateTime();
|
||||
|
||||
boolean areNeighborsLoaded();
|
||||
|
||||
@Nullable Chunk getNeighborChunk(int index);
|
||||
|
||||
CopyOnWriteArrayList<Short> getQueuedLightingUpdates(EnumSkyBlock type);
|
||||
|
||||
List<Chunk> getNeighbors();
|
||||
|
||||
void setNeighborChunk(int index, @Nullable Chunk chunk);
|
||||
|
||||
void setLightUpdateTime(long time);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package io.akarin.api.internal.mixin;
|
||||
|
||||
public interface IMixinLockProvider {
|
||||
public Object lock();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -1,16 +1,9 @@
|
||||
package io.akarin.api.internal.mixin;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.EnumSkyBlock;
|
||||
import java.util.Random;
|
||||
|
||||
public interface IMixinWorldServer {
|
||||
boolean updateLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk chunk);
|
||||
|
||||
boolean checkLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors);
|
||||
|
||||
ExecutorService getLightingExecutor();
|
||||
public Object tickLock();
|
||||
public Random rand();
|
||||
public Object trackLock();
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package io.akarin.api.internal.utils;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
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.
|
||||
*/
|
||||
private final AtomicBoolean writeLocked = new AtomicBoolean(false);
|
||||
|
||||
// --------- Thread local restricted fields ---------
|
||||
private long heldThreadId = 0;
|
||||
private int reentrantLocks = 0;
|
||||
|
||||
/**
|
||||
* Lock as a typical write lock
|
||||
*/
|
||||
public void lock() {
|
||||
long currentThreadId = Thread.currentThread().getId();
|
||||
if (heldThreadId == currentThreadId) {
|
||||
reentrantLocks++;
|
||||
} else {
|
||||
while (!writeLocked.compareAndSet(false, true)) ; // In case acquire one lock concurrently
|
||||
heldThreadId = currentThreadId;
|
||||
}
|
||||
}
|
||||
|
||||
public void unlock() {
|
||||
if (reentrantLocks == 0) {
|
||||
heldThreadId = 0;
|
||||
if (readerThreads.get() == 0 || readerThreads.getAndDecrement() == 1) { // Micro-optimization: this saves one subtract
|
||||
writeLocked.set(false);
|
||||
}
|
||||
} else {
|
||||
--reentrantLocks;
|
||||
}
|
||||
}
|
||||
|
||||
private final AtomicInteger readerThreads = new AtomicInteger(0);
|
||||
|
||||
/**
|
||||
* Lock as a typical read lock
|
||||
*/
|
||||
public void lockWeak() {
|
||||
long currentThreadId = Thread.currentThread().getId();
|
||||
if (heldThreadId == currentThreadId) {
|
||||
reentrantLocks++;
|
||||
} else {
|
||||
if (readerThreads.get() == 0) {
|
||||
while (!writeLocked.compareAndSet(false, true)) ; // Block future write lock
|
||||
}
|
||||
heldThreadId = currentThreadId;
|
||||
readerThreads.getAndIncrement(); // Micro-optimization: this saves one plus
|
||||
}
|
||||
}
|
||||
|
||||
public void unlockWeak() {
|
||||
if (reentrantLocks == 0) {
|
||||
heldThreadId = 0;
|
||||
writeLocked.set(false);
|
||||
} else {
|
||||
--reentrantLocks;
|
||||
}
|
||||
}
|
||||
|
||||
// --------- Wrappers to allow typical usages ---------
|
||||
private SpinningWriteLock wrappedWriteLock = new SpinningWriteLock();
|
||||
private SpinningReadLock wrappedReadLock = new SpinningReadLock();
|
||||
|
||||
public class SpinningWriteLock {
|
||||
public void lock() {
|
||||
lock();
|
||||
}
|
||||
public void unlock() {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public class SpinningReadLock {
|
||||
public void lock() {
|
||||
lockWeak();
|
||||
}
|
||||
public void unlock() {
|
||||
unlockWeak();
|
||||
}
|
||||
}
|
||||
|
||||
public SpinningWriteLock writeLock() {
|
||||
return wrappedWriteLock;
|
||||
}
|
||||
|
||||
public SpinningReadLock readLocked() {
|
||||
return wrappedReadLock;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.akarin.api.internal.utils.thread;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class OpenExecutionException extends ExecutionException {
|
||||
private static final long serialVersionUID = 7830266012832686185L;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* Written by Doug Lea with assistance from members of JCP JSR-166
|
||||
* Expert Group and released to the public domain, as explained at
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
package io.akarin.api.internal.utils.thread;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletionService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RunnableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class SuspendableExecutorCompletionService<V> implements CompletionService<V> {
|
||||
private final SuspendableThreadPoolExecutor executor;
|
||||
private final BlockingQueue<Future<V>> completionQueue;
|
||||
|
||||
public void suspend() {
|
||||
executor.suspend();
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
executor.resume();
|
||||
}
|
||||
|
||||
/**
|
||||
* FutureTask extension to enqueue upon completion
|
||||
*/
|
||||
private class QueueingFuture extends FutureTask<Void> {
|
||||
QueueingFuture(RunnableFuture<V> task) {
|
||||
super(task, null);
|
||||
this.task = task;
|
||||
}
|
||||
protected void done() { completionQueue.add(task); }
|
||||
private final Future<V> task;
|
||||
}
|
||||
|
||||
private RunnableFuture<V> newTaskFor(Callable<V> task) {
|
||||
return new FutureTask<V>(task);
|
||||
}
|
||||
|
||||
private RunnableFuture<V> newTaskFor(Runnable task, V result) {
|
||||
return new FutureTask<V>(task, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ExecutorCompletionService using the supplied
|
||||
* executor for base task execution and a
|
||||
* {@link LinkedBlockingQueue} as a completion queue.
|
||||
*
|
||||
* @param executor the executor to use
|
||||
* @throws NullPointerException if executor is {@code null}
|
||||
*/
|
||||
public SuspendableExecutorCompletionService(SuspendableThreadPoolExecutor executor) {
|
||||
if (executor == null)
|
||||
throw new NullPointerException();
|
||||
this.executor = executor;
|
||||
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ExecutorCompletionService using the supplied
|
||||
* executor for base task execution and the supplied queue as its
|
||||
* completion queue.
|
||||
*
|
||||
* @param executor the executor to use
|
||||
* @param completionQueue the queue to use as the completion queue
|
||||
* normally one dedicated for use by this service. This
|
||||
* queue is treated as unbounded -- failed attempted
|
||||
* {@code Queue.add} operations for completed tasks cause
|
||||
* them not to be retrievable.
|
||||
* @throws NullPointerException if executor or completionQueue are {@code null}
|
||||
*/
|
||||
public SuspendableExecutorCompletionService(SuspendableThreadPoolExecutor executor,
|
||||
BlockingQueue<Future<V>> completionQueue) {
|
||||
if (executor == null || completionQueue == null)
|
||||
throw new NullPointerException();
|
||||
this.executor = executor;
|
||||
this.completionQueue = completionQueue;
|
||||
}
|
||||
|
||||
public Future<V> submit(Callable<V> task) {
|
||||
if (task == null) throw new NullPointerException();
|
||||
RunnableFuture<V> f = newTaskFor(task);
|
||||
executor.execute(new QueueingFuture(f));
|
||||
return f;
|
||||
}
|
||||
|
||||
public Future<V> submit(Runnable task, V result) {
|
||||
if (task == null) throw new NullPointerException();
|
||||
RunnableFuture<V> f = newTaskFor(task, result);
|
||||
executor.execute(new QueueingFuture(f));
|
||||
return f;
|
||||
}
|
||||
|
||||
public Future<V> take() throws InterruptedException {
|
||||
return completionQueue.take();
|
||||
}
|
||||
|
||||
public Future<V> poll() {
|
||||
return completionQueue.poll();
|
||||
}
|
||||
|
||||
public Future<V> poll(long timeout, TimeUnit unit)
|
||||
throws InterruptedException {
|
||||
return completionQueue.poll(timeout, unit);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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,16 +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 boolean legacyWorldTimings;
|
||||
private static void legacyWorldTimings() {
|
||||
legacyWorldTimings = getBoolean("alternative.legacy-world-timings-required", false);
|
||||
}
|
||||
|
||||
public static long timeUpdateInterval;
|
||||
private static void timeUpdateInterval() {
|
||||
timeUpdateInterval = getSeconds(getString("core.tick-rate.world-time-update-interval", "1s")) * 10;
|
||||
@@ -185,16 +175,6 @@ public class AkarinGlobalConfig {
|
||||
keepAliveTimeout = getSeconds(getString("core.keep-alive-response-timeout", "30s")) * 1000;
|
||||
}
|
||||
|
||||
public static int asyncLightingThreads;
|
||||
private static void asyncLightingThreads() {
|
||||
asyncLightingThreads = getInt("core.async-lighting.executor-threads", 4);
|
||||
}
|
||||
|
||||
public static boolean asyncLightingWorkStealing;
|
||||
private static void asyncLightingWorkStealing() {
|
||||
asyncLightingWorkStealing = getBoolean("core.async-lighting.use-work-stealing", false);
|
||||
}
|
||||
|
||||
public static boolean throwOnAsyncCaught;
|
||||
private static void throwOnAsyncCaught() {
|
||||
throwOnAsyncCaught = getBoolean("core.thread-safe.async-catcher.throw-on-caught", true);
|
||||
@@ -210,56 +190,6 @@ public class AkarinGlobalConfig {
|
||||
noResponseDoGC = getBoolean("alternative.gc-before-stuck-restart", true);
|
||||
}
|
||||
|
||||
public static String messageKick;
|
||||
private static void messageKick() {
|
||||
messageKick = getString("messages.disconnect.kick-player", "Kicked by an operator.");
|
||||
}
|
||||
|
||||
public static String messageBan;
|
||||
private static void messageBan() {
|
||||
messageBan = getString("messages.disconnect.ban-player-name", "You are banned from this server! %s %s");
|
||||
}
|
||||
|
||||
public static String messageBanReason;
|
||||
private static void messageBanReason() {
|
||||
messageBanReason = getString("messages.disconnect.ban-reason", "\nReason: ");
|
||||
}
|
||||
|
||||
public static String messageBanExpires;
|
||||
private static void messageBanExpires() {
|
||||
messageBanExpires = getString("messages.disconnect.ban-expires", "\nYour ban will be removed on ");
|
||||
}
|
||||
|
||||
public static String messageBanIp;
|
||||
private static void messageBanIp() {
|
||||
messageBanIp = getString("messages.disconnect.ban-player-ip", "Your IP address is banned from this server! %s %s");
|
||||
}
|
||||
|
||||
public static String messageDupLogin;
|
||||
private static void messageDupLogin() {
|
||||
messageDupLogin = getString("messages.disconnect.kick-player-duplicate-login", "You logged in from another location");
|
||||
}
|
||||
|
||||
public static String messageJoin;
|
||||
private static void messageJoin() {
|
||||
messageJoin = getString("messages.connect.player-join-server", "§e%s joined the game");
|
||||
}
|
||||
|
||||
public static String messageJoinRenamed;
|
||||
private static void messageJoinRenamed() {
|
||||
messageJoinRenamed = getString("messages.connect.renamed-player-join-server", "§e%s (formerly known as %s) joined the game");
|
||||
}
|
||||
|
||||
public static String messageKickKeepAlive;
|
||||
private static void messagekickKeepAlive() {
|
||||
messageKickKeepAlive = getString("messages.disconnect.kick-player-timeout-keep-alive", "Timed out");
|
||||
}
|
||||
|
||||
public static String messagePlayerQuit;
|
||||
private static void messagePlayerQuit() {
|
||||
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", "");
|
||||
@@ -294,4 +224,14 @@ public class AkarinGlobalConfig {
|
||||
private static void forceHardcoreDifficulty() {
|
||||
forceHardcoreDifficulty = getBoolean("alternative.force-difficulty-on-hardcore", true);
|
||||
}
|
||||
|
||||
public static int fileIOThreads;
|
||||
private static void fileIOThreads() {
|
||||
fileIOThreads = getInt("core.chunk-save-threads", 2);
|
||||
}
|
||||
|
||||
public static int parallelMode;
|
||||
private static void parallelMode() {
|
||||
parallelMode = getInt("core.parallel-mode", 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.spigotmc.RestartCommand;
|
||||
import org.spigotmc.WatchdogThread;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
@@ -23,6 +22,10 @@ import net.minecraft.server.MinecraftServer;
|
||||
public abstract class Watchcat extends Thread {
|
||||
@Shadow private static WatchdogThread instance;
|
||||
@Shadow private @Final long timeoutTime;
|
||||
@Shadow private @Final long earlyWarningEvery; // Paper - Timeout time for just printing a dump but not restarting
|
||||
@Shadow private @Final long earlyWarningDelay; // Paper
|
||||
@Shadow public static volatile boolean hasStarted; // Paper
|
||||
@Shadow private long lastEarlyWarning; // Paper - Keep track of short dump times to avoid spamming console with short dumps
|
||||
@Shadow private @Final boolean restart;
|
||||
@Shadow private volatile long lastTick;
|
||||
@Shadow private volatile boolean stopping;
|
||||
@@ -38,48 +41,73 @@ public abstract class Watchcat extends Thread {
|
||||
@Overwrite
|
||||
public void run() {
|
||||
while (!stopping) {
|
||||
//
|
||||
if (lastTick != 0 && System.currentTimeMillis() > lastTick + timeoutTime && !Boolean.getBoolean("disable.watchdog")) { // Paper - Add property to disable
|
||||
Logger log = Bukkit.getServer().getLogger();
|
||||
log.log(Level.SEVERE, "Server has stopped responding!");
|
||||
log.log(Level.SEVERE, "Please report this to https://github.com/Akarin-project/Akarin/issues");
|
||||
log.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports");
|
||||
log.log(Level.SEVERE, "Akarin version: " + Bukkit.getServer().getVersion());
|
||||
//
|
||||
if (net.minecraft.server.World.haveWeSilencedAPhysicsCrash) {
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
log.log(Level.SEVERE, "During the run of the server, a physics stackoverflow was supressed");
|
||||
log.log(Level.SEVERE, "near " + net.minecraft.server.World.blockLocation);
|
||||
}
|
||||
// Paper start - Warn in watchdog if an excessive velocity was ever set
|
||||
if (CraftServer.excessiveVelEx != null) {
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
log.log(Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity");
|
||||
log.log(Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated");
|
||||
log.log(Level.SEVERE, CraftServer.excessiveVelEx.getMessage());
|
||||
for (StackTraceElement stack : CraftServer.excessiveVelEx.getStackTrace()) {
|
||||
log.log(Level.SEVERE, "\t\t" + stack);
|
||||
}
|
||||
}
|
||||
// Paper start
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if ( lastTick != 0 && currentTime > lastTick + earlyWarningEvery && !Boolean.getBoolean("disable.watchdog") )
|
||||
{
|
||||
boolean isLongTimeout = currentTime > lastTick + timeoutTime;
|
||||
// Don't spam early warning dumps
|
||||
if (!isLongTimeout && (earlyWarningEvery <= 0 || !hasStarted || currentTime < lastEarlyWarning + earlyWarningEvery || currentTime < lastTick + earlyWarningDelay))
|
||||
continue;
|
||||
lastEarlyWarning = currentTime;
|
||||
// Paper end
|
||||
Logger log = Bukkit.getServer().getLogger();
|
||||
// Paper start - Different message when it's a short timeout
|
||||
if (isLongTimeout) {
|
||||
log.log(Level.SEVERE, "The server has stopped responding!");
|
||||
log.log(Level.SEVERE, "Please report this to https://github.com/Akarin-project/Akarin/issues"); // Akarin
|
||||
log.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports");
|
||||
log.log(Level.SEVERE, "Akarin version: " + Bukkit.getServer().getVersion()); // Akarin
|
||||
//
|
||||
if (net.minecraft.server.World.haveWeSilencedAPhysicsCrash) {
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
log.log(Level.SEVERE, "During the run of the server, a physics stackoverflow was supressed");
|
||||
log.log(Level.SEVERE, "near " + net.minecraft.server.World.blockLocation);
|
||||
}
|
||||
// Paper start - Warn in watchdog if an excessive velocity was ever set
|
||||
if (org.bukkit.craftbukkit.CraftServer.excessiveVelEx != null) {
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
log.log(Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity");
|
||||
log.log(Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated");
|
||||
log.log(Level.SEVERE, org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getMessage());
|
||||
for (StackTraceElement stack : org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getStackTrace()) {
|
||||
log.log(Level.SEVERE, "\t\t" + stack);
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
} else {
|
||||
// log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---"); // Akarin
|
||||
log.log(Level.SEVERE, "The server has not responded for " + (currentTime - lastTick) / 1000 + " seconds! Creating thread dump");
|
||||
}
|
||||
// Paper end - Different message for short timeout
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
log.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Akarin!):");
|
||||
dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().primaryThread.getId(), Integer.MAX_VALUE), log);
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
//
|
||||
log.log(Level.SEVERE, "Entire Thread Dump:");
|
||||
ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
|
||||
for (ThreadInfo thread : threads) {
|
||||
dumpThread(thread, log);
|
||||
// Paper start - Only print full dump on long timeouts
|
||||
if (isLongTimeout) {
|
||||
log.log(Level.SEVERE, "Entire Thread Dump:");
|
||||
ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
|
||||
for (ThreadInfo thread : threads) {
|
||||
dumpThread(thread, log);
|
||||
}
|
||||
} else {
|
||||
// log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---"); // Akarin
|
||||
}
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
|
||||
if (restart) RestartCommand.restart(); // GC Inlined
|
||||
break;
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
|
||||
if ( isLongTimeout )
|
||||
{
|
||||
if (restart) {
|
||||
RestartCommand.restart();
|
||||
}
|
||||
break;
|
||||
} // Paper end
|
||||
}
|
||||
|
||||
try {
|
||||
sleep(9000); // Akarin
|
||||
sleep(1000); // Paper - Reduce check time to every second instead of every ten seconds, more consistent and allows for short timeout
|
||||
} catch (InterruptedException ex) {
|
||||
interrupt();
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package io.akarin.server.mixin.core;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import com.destroystokyo.paper.PaperConfig;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import net.minecraft.server.FileIOThread;
|
||||
import net.minecraft.server.IAsyncChunkSaver;
|
||||
|
||||
@Mixin(value = FileIOThread.class, remap = false)
|
||||
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 = "f") private volatile boolean isAwaitFinish;
|
||||
|
||||
@Overwrite // OBFHELPER: saveChunk
|
||||
public void a(IAsyncChunkSaver iasyncchunksaver) {
|
||||
queuedChunkCounter.incrementAndGet();
|
||||
executor.execute(() -> writeChunk(iasyncchunksaver));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a chunk, re-add to the queue if unsuccessful
|
||||
*/
|
||||
private void writeChunk(IAsyncChunkSaver iasyncchunksaver) {
|
||||
if (!iasyncchunksaver.a()) { // PAIL: WriteNextIO() -> Returns if the write was unsuccessful
|
||||
queuedChunkCounter.decrementAndGet();
|
||||
|
||||
if (PaperConfig.enableFileIOThreadSleep) {
|
||||
try {
|
||||
Thread.sleep(isAwaitFinish ? 0L : 2L);
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
writeChunk(iasyncchunksaver);
|
||||
}
|
||||
}
|
||||
|
||||
@Overwrite // OBFHELPER: waitForFinish
|
||||
public void b() throws InterruptedException {
|
||||
isAwaitFinish = true;
|
||||
while (queuedChunkCounter.get() != 0) Thread.sleep(9L);
|
||||
isAwaitFinish = false;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package io.akarin.server.mixin.core;
|
||||
|
||||
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.server.core.AkarinGlobalConfig;
|
||||
import net.minecraft.server.Block;
|
||||
import net.minecraft.server.Blocks;
|
||||
import net.minecraft.server.ItemMonsterEgg;
|
||||
|
||||
@Mixin(value = ItemMonsterEgg.class, remap = false)
|
||||
public abstract class MixinItemMonsterEgg {
|
||||
@Redirect(method = "a*", at = @At(
|
||||
value = "FIELD",
|
||||
target = "net/minecraft/server/Blocks.MOB_SPAWNER:Lnet/minecraft/server/Block;",
|
||||
opcode = Opcodes.GETSTATIC
|
||||
))
|
||||
private boolean configurable(Block target) {
|
||||
return target == Blocks.MOB_SPAWNER && AkarinGlobalConfig.allowSpawnerModify;
|
||||
}
|
||||
}
|
||||
@@ -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,16 +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.ExecutorCompletionService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
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;
|
||||
@@ -22,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.IMixinLockProvider;
|
||||
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;
|
||||
@@ -42,6 +40,7 @@ import net.minecraft.server.WorldServer;
|
||||
@Mixin(value = MinecraftServer.class, remap = false)
|
||||
public abstract class MixinMinecraftServer {
|
||||
@Shadow @Final public Thread primaryThread;
|
||||
private int cachedWorldSize;
|
||||
|
||||
@Overwrite
|
||||
public String getServerModName() {
|
||||
@@ -50,16 +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 = 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();
|
||||
}
|
||||
@@ -75,80 +76,26 @@ 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()));
|
||||
|
||||
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 {
|
||||
world.timings.tickEntities.startTiming();
|
||||
world.tickEntities();
|
||||
world.timings.tickEntities.stopTiming();
|
||||
world.getTracker().updatePlayers();
|
||||
world.explosionDensityCache.clear(); // Paper - Optimize explosions
|
||||
} catch (Throwable throwable) {
|
||||
CrashReport crashreport;
|
||||
try {
|
||||
@@ -162,9 +109,11 @@ public abstract class MixinMinecraftServer {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void tickWorld(WorldServer world) {
|
||||
private void tickWorld(WorldServer world, BooleanSupplier supplier) {
|
||||
try {
|
||||
world.doTick();
|
||||
world.timings.doTick.startTiming();
|
||||
world.doTick(supplier);
|
||||
world.timings.doTick.stopTiming();
|
||||
} catch (Throwable throwable) {
|
||||
CrashReport crashreport;
|
||||
try {
|
||||
@@ -178,7 +127,7 @@ public abstract class MixinMinecraftServer {
|
||||
}
|
||||
|
||||
@Overwrite
|
||||
public void D() throws InterruptedException {
|
||||
public void b(BooleanSupplier supplier) throws InterruptedException, ExecutionException {
|
||||
Runnable runnable;
|
||||
MinecraftTimings.bukkitSchedulerTimer.startTiming();
|
||||
this.server.getScheduler().mainThreadHeartbeat(this.ticks);
|
||||
@@ -186,12 +135,16 @@ public abstract class MixinMinecraftServer {
|
||||
|
||||
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();
|
||||
@@ -200,78 +153,105 @@ public abstract class MixinMinecraftServer {
|
||||
ChunkIOExecutor.tick();
|
||||
MinecraftTimings.chunkIOTickTimer.stopTiming();
|
||||
|
||||
Akari.worldTiming.startTiming();
|
||||
if (AkarinGlobalConfig.legacyWorldTimings) {
|
||||
for (int i = 0; i < worlds.size(); ++i) {
|
||||
WorldServer world = worlds.get(i);
|
||||
world.timings.tickEntities.startTiming();
|
||||
world.timings.doTick.startTiming();
|
||||
}
|
||||
}
|
||||
Akari.STAGE_TICK.submit(() -> {
|
||||
// Never tick one world concurrently!
|
||||
for (int i = 1; i <= worlds.size(); ++i) {
|
||||
WorldServer world = worlds.get(i < worlds.size() ? i : 0);
|
||||
synchronized (((IMixinLockProvider) world).lock()) {
|
||||
if (cachedWorldSize != worldServer.size()) Akari.resizeTickExecutors((cachedWorldSize = worldServer.size()));
|
||||
switch (AkarinGlobalConfig.parallelMode) {
|
||||
case 1:
|
||||
case 2:
|
||||
default:
|
||||
// Never tick one world concurrently!
|
||||
WorldServer interlacedWorld = null;
|
||||
for (WorldServer world : worldServer.values()) {
|
||||
if (interlacedWorld == null) {
|
||||
interlacedWorld = world;
|
||||
} else {
|
||||
Akari.STAGE_TICK.submit(() -> {
|
||||
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 (WorldServer world : worldServer.values()) {
|
||||
synchronized (((IMixinWorldServer) world).tickLock()) {
|
||||
tickWorld(world, supplier);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
for (int i = (AkarinGlobalConfig.parallelMode == 1 ? cachedWorldSize + 1 : cachedWorldSize * 2); i --> 0 ;) {
|
||||
Akari.STAGE_TICK.take();
|
||||
}
|
||||
|
||||
break;
|
||||
case 0:
|
||||
Akari.STAGE_TICK.submit(() -> {
|
||||
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 (WorldServer world : worldServer.values()) {
|
||||
synchronized (((IMixinWorldServer) world).tickLock()) {
|
||||
tickWorld(world, supplier);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
Akari.STAGE_TICK.take();
|
||||
Akari.STAGE_TICK.take();
|
||||
break;
|
||||
case -1:
|
||||
for (WorldServer world : worldServer.values()) {
|
||||
tickWorld(world, supplier);
|
||||
tickEntities(world);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
for (int i = 0; i < worlds.size(); ++i) {
|
||||
WorldServer world = worlds.get(i);
|
||||
synchronized (((IMixinLockProvider) world).lock()) {
|
||||
tickWorld(world);
|
||||
world.explosionDensityCache.clear(); // Paper - Optimize explosions
|
||||
}
|
||||
}
|
||||
|
||||
Akari.entityCallbackTiming.startTiming();
|
||||
Akari.STAGE_TICK.take();
|
||||
Akari.entityCallbackTiming.stopTiming();
|
||||
|
||||
Akari.worldTiming.stopTiming();
|
||||
if (AkarinGlobalConfig.legacyWorldTimings) {
|
||||
for (int i = 0; i < worlds.size(); ++i) {
|
||||
WorldServer world = worlds.get(i);
|
||||
world.timings.tickEntities.stopTiming();
|
||||
world.timings.doTick.stopTiming();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Akari.callbackTiming.startTiming();
|
||||
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
|
||||
Akari.callbackTiming.stopTiming();
|
||||
|
||||
for (int i = 0; i < worlds.size(); ++i) {
|
||||
WorldServer world = worlds.get(i);
|
||||
tickUnsafeSync(world);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public void tickUnsafeSync(WorldServer world) {
|
||||
world.timings.doChunkMap.startTiming();
|
||||
world.manager.flush();
|
||||
world.timings.doChunkMap.stopTiming();
|
||||
|
||||
world.getTracker().updatePlayers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,72 +1,32 @@
|
||||
package io.akarin.server.mixin.core;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import co.aikar.timings.Timing;
|
||||
import io.akarin.api.internal.Akari;
|
||||
import io.akarin.api.internal.Akari.AssignableThread;
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
@Mixin(targets = "co.aikar.timings.TimingHandler", remap = false)
|
||||
public abstract class MixinTimingHandler {
|
||||
@Shadow @Final String name;
|
||||
@Shadow private boolean enabled;
|
||||
@Shadow private volatile long start;
|
||||
@Shadow private volatile int timingDepth;
|
||||
@Shadow private AtomicLong start;
|
||||
@Shadow private AtomicInteger timingDepth;
|
||||
|
||||
@Shadow abstract void addDiff(long diff);
|
||||
@Shadow public abstract Timing startTiming();
|
||||
|
||||
@Overwrite
|
||||
public Timing startTimingIfSync() {
|
||||
if (Akari.isPrimaryThread(false)) {
|
||||
startTiming();
|
||||
}
|
||||
return (Timing) this;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Inject(method = "startTiming", at = @At("HEAD"), cancellable = true)
|
||||
public void onStartTiming(CallbackInfoReturnable ci) {
|
||||
if (!Akari.isPrimaryThread(false)) ci.setReturnValue(this); // Avoid modify any field
|
||||
}
|
||||
|
||||
@Overwrite
|
||||
public void stopTimingIfSync() {
|
||||
if (Akari.isPrimaryThread(false)) {
|
||||
stopTiming(true); // Avoid twice thread check
|
||||
}
|
||||
}
|
||||
|
||||
@Overwrite
|
||||
public void stopTiming() {
|
||||
stopTiming(false);
|
||||
}
|
||||
|
||||
public void stopTiming(boolean alreadySync) {
|
||||
Thread curThread = Thread.currentThread();
|
||||
if (!enabled || curThread instanceof AssignableThread) return;
|
||||
if (!alreadySync && 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 || timingDepth.decrementAndGet() != 0) return;
|
||||
long prev = start.getAndSet(0); // Akarin
|
||||
addDiff(System.nanoTime() - prev); // Akarin
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import net.minecraft.server.MCUtil;
|
||||
|
||||
@Mixin(value = VersionCommand.class, remap = false)
|
||||
public abstract class MixinVersionCommand {
|
||||
@Shadow private static int getFromRepo(String repo, String hash) { return 0; }
|
||||
@Shadow private static int getFromRepo(String repo, String branch, String hash) { return 0; }
|
||||
|
||||
/**
|
||||
* Match current version with repository and calculate the distance
|
||||
@@ -26,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,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;
|
||||
@@ -82,7 +82,7 @@ public abstract class MixinVersionCommand {
|
||||
}
|
||||
}
|
||||
if (!hasVersion) {
|
||||
obtainVersion(sender);
|
||||
obtainVersionAsync(sender);
|
||||
if (AkarinGlobalConfig.legacyVersioningCompat) currentSender = sender;
|
||||
}
|
||||
}
|
||||
@@ -90,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!");
|
||||
@@ -99,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,12 +17,12 @@ public abstract class MixinWorldManager {
|
||||
|
||||
@Overwrite
|
||||
public void a(Entity entity) {
|
||||
this.world.getTracker().entriesLock.lock(); // Akarin
|
||||
this.world.getTracker().track(entity);
|
||||
this.world.getTracker().entriesLock.unlock(); // Akarin
|
||||
synchronized (((IMixinWorldServer) this.world).trackLock()) { // Akarin
|
||||
this.world.getTracker().track(entity);
|
||||
}
|
||||
|
||||
if (entity instanceof EntityPlayer) {
|
||||
this.world.worldProvider.a((EntityPlayer) entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,44 @@
|
||||
package io.akarin.server.mixin.core;
|
||||
|
||||
import java.util.Random;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
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.IMixinLockProvider;
|
||||
import io.akarin.api.internal.mixin.IMixinWorldServer;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
@Mixin(value = WorldServer.class, remap = false)
|
||||
public abstract class MixinWorldServer implements IMixinLockProvider {
|
||||
@Redirect(method = "doTick()V", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "net/minecraft/server/PlayerChunkMap.flush()V"
|
||||
))
|
||||
public void onFlush() {} // Migrated to main thread
|
||||
|
||||
public abstract class MixinWorldServer implements IMixinWorldServer {
|
||||
private final Object tickLock = new Object();
|
||||
|
||||
@Override
|
||||
public Object lock() {
|
||||
public Object tickLock() {
|
||||
return tickLock;
|
||||
}
|
||||
|
||||
private final Random sharedRandom = new io.akarin.api.internal.utils.random.LightRandom() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private boolean locked = false;
|
||||
@Override
|
||||
public synchronized void setSeed(long seed) {
|
||||
if (locked) {
|
||||
LogManager.getLogger().error("Ignoring setSeed on Entity.SHARED_RANDOM", new Throwable());
|
||||
} else {
|
||||
super.setSeed(seed);
|
||||
locked = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public Random rand() {
|
||||
return sharedRandom;
|
||||
}
|
||||
|
||||
public final Object trackLock = new Object();
|
||||
|
||||
@Override
|
||||
public Object trackLock() {
|
||||
return trackLock;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,130 +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.cps;
|
||||
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
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.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import io.akarin.api.internal.mixin.IMixinChunk;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.EnumDirection;
|
||||
import net.minecraft.server.MCUtil;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
@Mixin(value = Chunk.class, remap = false)
|
||||
public abstract class MixinChunk implements IMixinChunk {
|
||||
private Chunk[] neighborChunks = new Chunk[4];
|
||||
private static final EnumDirection[] CARDINAL_DIRECTIONS = new EnumDirection[] {EnumDirection.NORTH, EnumDirection.SOUTH, EnumDirection.EAST, EnumDirection.WEST};
|
||||
|
||||
@Shadow @Final public World world;
|
||||
@Shadow @Final public int locX;
|
||||
@Shadow @Final public int locZ;
|
||||
|
||||
@Override
|
||||
public Chunk getNeighborChunk(int index) {
|
||||
return this.neighborChunks[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNeighborChunk(int index, @Nullable Chunk chunk) {
|
||||
this.neighborChunks[index] = chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Chunk> getNeighbors() {
|
||||
List<Chunk> neighborList = Lists.newArrayList();
|
||||
for (Chunk neighbor : this.neighborChunks) {
|
||||
if (neighbor != null) {
|
||||
neighborList.add(neighbor);
|
||||
}
|
||||
}
|
||||
return neighborList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areNeighborsLoaded() {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (this.neighborChunks[i] == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int directionToIndex(EnumDirection direction) {
|
||||
switch (direction) {
|
||||
case NORTH:
|
||||
return 0;
|
||||
case SOUTH:
|
||||
return 1;
|
||||
case EAST:
|
||||
return 2;
|
||||
case WEST:
|
||||
return 3;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected direction");
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "addEntities", at = @At("RETURN"))
|
||||
public void onLoadReturn(CallbackInfo ci) {
|
||||
BlockPosition origin = new BlockPosition(locX, 0, locZ);
|
||||
for (EnumDirection direction : CARDINAL_DIRECTIONS) {
|
||||
BlockPosition shift = origin.shift(direction);
|
||||
Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), shift.getX(), shift.getZ());
|
||||
if (neighbor != null) {
|
||||
int neighborIndex = directionToIndex(direction);
|
||||
int oppositeNeighborIndex = directionToIndex(direction.opposite());
|
||||
this.setNeighborChunk(neighborIndex, neighbor);
|
||||
((IMixinChunk) neighbor).setNeighborChunk(oppositeNeighborIndex, (Chunk) (Object) this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "removeEntities", at = @At("RETURN"))
|
||||
public void onUnload(CallbackInfo ci) {
|
||||
BlockPosition origin = new BlockPosition(locX, 0, locZ);
|
||||
for (EnumDirection direction : CARDINAL_DIRECTIONS) {
|
||||
BlockPosition shift = origin.shift(direction);
|
||||
Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), shift.getX(), shift.getZ());
|
||||
if (neighbor != null) {
|
||||
int neighborIndex = directionToIndex(direction);
|
||||
int oppositeNeighborIndex = directionToIndex(direction.opposite());
|
||||
this.setNeighborChunk(neighborIndex, null);
|
||||
((IMixinChunk) neighbor).setNeighborChunk(oppositeNeighborIndex, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,87 +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) continue;
|
||||
|
||||
if (chunk.isUnloading()) {
|
||||
if (chunk.scheduledForUnload != null) {
|
||||
if (now - chunk.scheduledForUnload > unloadAfter) {
|
||||
chunk.scheduledForUnload = null;
|
||||
} else continue;
|
||||
}
|
||||
|
||||
if (!unloadChunk(chunk, true)) { // Event cancelled
|
||||
// If a plugin cancelled it, we shouldn't trying unload it for a while
|
||||
chunk.setShouldUnload(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
it.remove();
|
||||
if (--remainingChunks <= targetSize || activityAccountant.activityTimeIsExhausted()) break; // more slack since the target size not work as intended
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,671 +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.lighting;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
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.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import io.akarin.api.internal.Akari;
|
||||
import io.akarin.api.internal.mixin.IMixinChunk;
|
||||
import io.akarin.api.internal.mixin.IMixinWorldServer;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Blocks;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.ChunkSection;
|
||||
import net.minecraft.server.EnumDirection;
|
||||
import net.minecraft.server.EnumSkyBlock;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.MCUtil;
|
||||
import net.minecraft.server.TileEntity;
|
||||
import net.minecraft.server.World;
|
||||
import net.minecraft.server.BlockPosition.MutableBlockPosition;
|
||||
|
||||
@Mixin(value = Chunk.class, remap = false, priority = 1001)
|
||||
public abstract class MixinChunk implements IMixinChunk {
|
||||
|
||||
// Keeps track of block positions in this chunk currently queued for sky light update
|
||||
private CopyOnWriteArrayList<Short> queuedSkyLightingUpdates = new CopyOnWriteArrayList<>();
|
||||
// Keeps track of block positions in this chunk currently queued for block light update
|
||||
private CopyOnWriteArrayList<Short> queuedBlockLightingUpdates = new CopyOnWriteArrayList<>();
|
||||
private AtomicInteger pendingLightUpdates = new AtomicInteger();
|
||||
private long lightUpdateTime;
|
||||
private static ExecutorService lightExecutorService;
|
||||
|
||||
@Shadow(aliases = "m") private boolean isGapLightingUpdated;
|
||||
@Shadow(aliases = "r") private boolean ticked;
|
||||
@Shadow @Final private ChunkSection[] sections;
|
||||
@Shadow @Final public int locX;
|
||||
@Shadow @Final public int locZ;
|
||||
@Shadow @Final public World world;
|
||||
@Shadow @Final public int[] heightMap;
|
||||
/** Which columns need their skylightMaps updated. */
|
||||
@Shadow(aliases = "i") @Final private boolean[] updateSkylightColumns;
|
||||
/** Queue containing the BlockPosition of tile entities queued for creation */
|
||||
@Shadow(aliases = "y") @Final private ConcurrentLinkedQueue<BlockPosition> tileEntityPosQueue;
|
||||
/** Boolean value indicating if the terrain is populated. */
|
||||
@Shadow(aliases = "done") private boolean isTerrainPopulated;
|
||||
@Shadow(aliases = "lit") private boolean isLightPopulated;
|
||||
/** Lowest value in the heightmap. */
|
||||
@Shadow(aliases = "v") private int heightMapMinimum;
|
||||
|
||||
@Shadow(aliases = "b") public abstract int getHeightValue(int x, int z);
|
||||
@Shadow(aliases = "g") @Nullable public abstract TileEntity createNewTileEntity(BlockPosition pos);
|
||||
@Shadow(aliases = "a") @Nullable public abstract TileEntity getTileEntity(BlockPosition pos, Chunk.EnumTileEntityState state);
|
||||
@Shadow @Final public abstract IBlockData getBlockData(BlockPosition pos);
|
||||
@Shadow @Final public abstract IBlockData getBlockData(int x, int y, int z);
|
||||
@Shadow public abstract boolean isUnloading();
|
||||
/** Checks the height of a block next to a sky-visible block and schedules a lighting update as necessary */
|
||||
@Shadow(aliases = "b") public abstract void checkSkylightNeighborHeight(int x, int z, int maxValue);
|
||||
@Shadow(aliases = "a") public abstract void updateSkylightNeighborHeight(int x, int z, int startY, int endY);
|
||||
@Shadow(aliases = "z") public abstract void setSkylightUpdated();
|
||||
@Shadow(aliases = "g") public abstract int getTopFilledSegment();
|
||||
@Shadow public abstract void markDirty();
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
public void onConstruct(World worldIn, int x, int z, CallbackInfo ci) {
|
||||
lightExecutorService = ((IMixinWorldServer) worldIn).getLightingExecutor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtomicInteger getPendingLightUpdates() {
|
||||
return this.pendingLightUpdates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLightUpdateTime() {
|
||||
return this.lightUpdateTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLightUpdateTime(long time) {
|
||||
this.lightUpdateTime = time;
|
||||
}
|
||||
|
||||
@Inject(method = "b(Z)V", at = @At("HEAD"), cancellable = true)
|
||||
private void onTickHead(boolean skipRecheckGaps, CallbackInfo ci) {
|
||||
final List<Chunk> neighbors = this.getSurroundingChunks();
|
||||
if (this.isGapLightingUpdated && this.world.worldProvider.m() && !skipRecheckGaps && !neighbors.isEmpty()) { // OBFHELPER: hasSkyLight
|
||||
lightExecutorService.execute(() -> {
|
||||
this.recheckGapsAsync(neighbors);
|
||||
});
|
||||
this.isGapLightingUpdated = false;
|
||||
}
|
||||
|
||||
this.ticked = true;
|
||||
|
||||
if ((true || !this.isLightPopulated) && this.isTerrainPopulated && !neighbors.isEmpty()) {
|
||||
lightExecutorService.execute(() -> {
|
||||
this.checkLightAsync(neighbors);
|
||||
});
|
||||
// set to true to avoid requeuing the same task when not finished
|
||||
this.isLightPopulated = true;
|
||||
}
|
||||
|
||||
while (!this.tileEntityPosQueue.isEmpty()) {
|
||||
BlockPosition blockpos = this.tileEntityPosQueue.poll();
|
||||
|
||||
if (this.getTileEntity(blockpos, Chunk.EnumTileEntityState.CHECK) == null && this.getBlockData(blockpos).getBlock().isTileEntity()) { // OBFHELPER: getTileEntity
|
||||
TileEntity tileentity = this.createNewTileEntity(blockpos);
|
||||
this.world.setTileEntity(blockpos, tileentity);
|
||||
this.world.b(blockpos, blockpos); // OBFHELPER: markBlockRangeForRenderUpdate
|
||||
}
|
||||
}
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Redirect(method = "b(III)V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.getHighestBlockYAt(Lnet/minecraft/server/BlockPosition;)Lnet/minecraft/server/BlockPosition;"))
|
||||
private BlockPosition onCheckSkylightGetHeight(World world, BlockPosition pos) {
|
||||
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, null);
|
||||
if (chunk == null) {
|
||||
return BlockPosition.ZERO;
|
||||
}
|
||||
|
||||
return new BlockPosition(pos.getX(), chunk.b(pos.getX() & 15, pos.getZ() & 15), pos.getZ()); // OBFHELPER: getHeightValue
|
||||
}
|
||||
|
||||
@Redirect(method = "a(IIII)V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.areChunksLoaded(Lnet/minecraft/server/BlockPosition;I)Z"))
|
||||
private boolean onAreaLoadedSkyLightNeighbor(World world, BlockPosition pos, int radius) {
|
||||
return this.isAreaLoaded();
|
||||
}
|
||||
|
||||
@Redirect(method = "a(IIII)V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.c(Lnet/minecraft/server/EnumSkyBlock;Lnet/minecraft/server/BlockPosition;)Z"))
|
||||
private boolean onCheckLightForSkylightNeighbor(World world, EnumSkyBlock enumSkyBlock, BlockPosition pos) {
|
||||
return this.checkWorldLightFor(enumSkyBlock, pos);
|
||||
}
|
||||
|
||||
@Inject(method = "h(Z)V", at = @At("HEAD"), cancellable = true)
|
||||
private void onRecheckGaps(CallbackInfo ci) {
|
||||
if (this.world.getMinecraftServer().isStopped() || lightExecutorService.isShutdown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isUnloading()) {
|
||||
return;
|
||||
}
|
||||
final List<Chunk> neighborChunks = this.getSurroundingChunks();
|
||||
if (neighborChunks.isEmpty()) {
|
||||
this.isGapLightingUpdated = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Akari.isPrimaryThread()) {
|
||||
try {
|
||||
lightExecutorService.execute(() -> {
|
||||
this.recheckGapsAsync(neighborChunks);
|
||||
});
|
||||
} catch (RejectedExecutionException ex) {
|
||||
// This could happen if ServerHangWatchdog kills the server
|
||||
// between the start of the method and the execute() call.
|
||||
if (!this.world.getMinecraftServer().isStopped() && !lightExecutorService.isShutdown()) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.recheckGapsAsync(neighborChunks);
|
||||
}
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rechecks chunk gaps async.
|
||||
*
|
||||
* @param neighbors A thread-safe list of surrounding neighbor chunks
|
||||
*/
|
||||
private void recheckGapsAsync(List<Chunk> neighbors) {
|
||||
this.isLightPopulated = false;
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
if (this.updateSkylightColumns[i + j * 16]) {
|
||||
this.updateSkylightColumns[i + j * 16] = false;
|
||||
int k = this.getHeightValue(i, j);
|
||||
int l = this.locX * 16 + i;
|
||||
int i1 = this.locZ * 16 + j;
|
||||
int j1 = Integer.MAX_VALUE;
|
||||
|
||||
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
|
||||
final Chunk chunk = this.getLightChunk((l + enumfacing.getAdjacentX()) >> 4, (i1 + enumfacing.getAdjacentZ()) >> 4, neighbors);
|
||||
if (chunk == null || chunk.isUnloading()) {
|
||||
continue;
|
||||
}
|
||||
j1 = Math.min(j1, chunk.w()); // OBFHELPER: getLowestHeight
|
||||
}
|
||||
|
||||
this.checkSkylightNeighborHeight(l, i1, j1);
|
||||
|
||||
for (EnumDirection enumfacing1 : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
|
||||
this.checkSkylightNeighborHeight(l + enumfacing1.getAdjacentX(), i1 + enumfacing1.getAdjacentZ(), k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.isGapLightingUpdated = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(method = "n()V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.getType(Lnet/minecraft/server/BlockPosition;)Lnet/minecraft/server/IBlockData;"))
|
||||
private IBlockData onRelightChecksGetBlockData(World world, BlockPosition pos) {
|
||||
Chunk chunk = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), pos.getX() >> 4, pos.getZ() >> 4);
|
||||
|
||||
final IMixinChunk spongeChunk = (IMixinChunk) chunk;
|
||||
if (chunk == null || chunk.isUnloading() || !spongeChunk.areNeighborsLoaded()) {
|
||||
return Blocks.AIR.getBlockData();
|
||||
}
|
||||
|
||||
return chunk.getBlockData(pos);
|
||||
}
|
||||
|
||||
@Redirect(method = "n()V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.w(Lnet/minecraft/server/BlockPosition;)Z"))
|
||||
private boolean onRelightChecksCheckLight(World world, BlockPosition pos) {
|
||||
return this.checkWorldLight(pos);
|
||||
}
|
||||
|
||||
// Avoids grabbing chunk async during light check
|
||||
@Redirect(method = "e(II)Z", at = @At(value = "INVOKE", target = "net/minecraft/server/World.w(Lnet/minecraft/server/BlockPosition;)Z"))
|
||||
private boolean onCheckLightWorld(World world, BlockPosition pos) {
|
||||
return this.checkWorldLight(pos);
|
||||
}
|
||||
|
||||
@Inject(method = "o()V", at = @At("HEAD"), cancellable = true)
|
||||
private void checkLightHead(CallbackInfo ci) {
|
||||
if (this.world.getMinecraftServer().isStopped() || lightExecutorService.isShutdown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isUnloading()) {
|
||||
return;
|
||||
}
|
||||
final List<Chunk> neighborChunks = this.getSurroundingChunks();
|
||||
if (neighborChunks.isEmpty()) {
|
||||
this.isLightPopulated = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Akari.isPrimaryThread()) {
|
||||
try {
|
||||
lightExecutorService.execute(() -> {
|
||||
this.checkLightAsync(neighborChunks);
|
||||
});
|
||||
} catch (RejectedExecutionException ex) {
|
||||
// This could happen if ServerHangWatchdog kills the server
|
||||
// between the start of the method and the execute() call.
|
||||
if (!this.world.getMinecraftServer().isStopped() && !lightExecutorService.isShutdown()) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.checkLightAsync(neighborChunks);
|
||||
}
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks light async.
|
||||
*
|
||||
* @param neighbors A thread-safe list of surrounding neighbor chunks
|
||||
*/
|
||||
private void checkLightAsync(List<Chunk> neighbors) {
|
||||
this.isTerrainPopulated = true;
|
||||
this.isLightPopulated = true;
|
||||
BlockPosition blockpos = new BlockPosition(this.locX << 4, 0, this.locZ << 4);
|
||||
|
||||
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
|
||||
CHECK_LIGHT:
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
if (!this.checkLightAsync(i, j, neighbors)) {
|
||||
this.isLightPopulated = false;
|
||||
break CHECK_LIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isLightPopulated) {
|
||||
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
|
||||
int k = enumfacing.c() == EnumDirection.EnumAxisDirection.POSITIVE ? 16 : 1; // OBFHELPER: getAxisDirection
|
||||
final BlockPosition pos = blockpos.shift(enumfacing, k);
|
||||
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, neighbors);
|
||||
if (chunk == null) {
|
||||
continue;
|
||||
}
|
||||
chunk.a(enumfacing.opposite()); // OBFHELPER: checkLightSide
|
||||
}
|
||||
|
||||
this.setSkylightUpdated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks light async.
|
||||
*
|
||||
* @param x The x position of chunk
|
||||
* @param z The z position of chunk
|
||||
* @param neighbors A thread-safe list of surrounding neighbor chunks
|
||||
* @return True if light update was successful, false if not
|
||||
*/
|
||||
private boolean checkLightAsync(int x, int z, List<Chunk> neighbors) {
|
||||
int i = this.getTopFilledSegment();
|
||||
boolean flag = false;
|
||||
boolean flag1 = false;
|
||||
MutableBlockPosition blockpos$mutableblockpos = new MutableBlockPosition((this.locX << 4) + x, 0, (this.locZ << 4) + z);
|
||||
|
||||
for (int j = i + 16 - 1; j > this.world.getSeaLevel() || j > 0 && !flag1; --j) {
|
||||
blockpos$mutableblockpos.setValues(blockpos$mutableblockpos.getX(), j, blockpos$mutableblockpos.getZ());
|
||||
int k = this.getBlockData(blockpos$mutableblockpos).c(); // OBFHELPER: getLightOpacity
|
||||
|
||||
if (k == 255 && blockpos$mutableblockpos.getY() < this.world.getSeaLevel()) {
|
||||
flag1 = true;
|
||||
}
|
||||
|
||||
if (!flag && k > 0) {
|
||||
flag = true;
|
||||
} else if (flag && k == 0 && !this.checkWorldLight(blockpos$mutableblockpos, neighbors)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = blockpos$mutableblockpos.getY(); l > 0; --l) {
|
||||
blockpos$mutableblockpos.setValues(blockpos$mutableblockpos.getX(), l, blockpos$mutableblockpos.getZ());
|
||||
|
||||
if (this.getBlockData(blockpos$mutableblockpos).d() > 0) { // getLightValue
|
||||
this.checkWorldLight(blockpos$mutableblockpos, neighbors);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread-safe method to retrieve a chunk during async light updates.
|
||||
*
|
||||
* @param chunkX The x position of chunk.
|
||||
* @param chunkZ The z position of chunk.
|
||||
* @param neighbors A thread-safe list of surrounding neighbor chunks
|
||||
* @return The chunk if available, null if not
|
||||
*/
|
||||
private Chunk getLightChunk(int chunkX, int chunkZ, List<Chunk> neighbors) {
|
||||
final Chunk currentChunk = (Chunk) (Object) this;
|
||||
if (currentChunk.a(chunkX, chunkZ)) { // OBFHELPER: isAtLocation
|
||||
if (currentChunk.isUnloading()) {
|
||||
return null;
|
||||
}
|
||||
return currentChunk;
|
||||
}
|
||||
if (neighbors == null) {
|
||||
neighbors = this.getSurroundingChunks();
|
||||
if (neighbors.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
for (Chunk neighbor : neighbors) {
|
||||
if (neighbor.a(chunkX, chunkZ)) { // OBFHELPER: isAtLocation
|
||||
if (neighbor.isUnloading()) {
|
||||
return null;
|
||||
}
|
||||
return neighbor;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if surrounding chunks are loaded thread-safe.
|
||||
*
|
||||
* @return True if surrounded chunks are loaded, false if not
|
||||
*/
|
||||
private boolean isAreaLoaded() {
|
||||
if (!this.areNeighborsLoaded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// add diagonal chunks
|
||||
final Chunk southEastChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(2);
|
||||
if (southEastChunk == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Chunk southWestChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(3);
|
||||
if (southWestChunk == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Chunk northEastChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(2);
|
||||
if (northEastChunk == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Chunk northWestChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(3);
|
||||
if (northWestChunk == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets surrounding chunks thread-safe.
|
||||
*
|
||||
* @return The list of surrounding chunks, empty list if not loaded
|
||||
*/
|
||||
private List<Chunk> getSurroundingChunks() {
|
||||
if (!this.areNeighborsLoaded()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// add diagonal chunks
|
||||
final Chunk southEastChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(2);
|
||||
if (southEastChunk == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final Chunk southWestChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(3);
|
||||
if (southWestChunk == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final Chunk northEastChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(2);
|
||||
if (northEastChunk == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final Chunk northWestChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(3);
|
||||
if (northWestChunk == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Chunk> chunkList = this.getNeighbors();
|
||||
chunkList.add(southEastChunk);
|
||||
chunkList.add(southWestChunk);
|
||||
chunkList.add(northEastChunk);
|
||||
chunkList.add(northWestChunk);
|
||||
return chunkList;
|
||||
}
|
||||
|
||||
@Inject(method = "c(III)V", at = @At("HEAD"), cancellable = true)
|
||||
private void onRelightBlock(int x, int y, int z, CallbackInfo ci) {
|
||||
lightExecutorService.execute(() -> {
|
||||
this.relightBlockAsync(x, y, z);
|
||||
});
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Relight's a block async.
|
||||
*
|
||||
* @param x The x position
|
||||
* @param y The y position
|
||||
* @param z The z position
|
||||
*/
|
||||
private void relightBlockAsync(int x, int y, int z) {
|
||||
int i = this.heightMap[z << 4 | x] & 255;
|
||||
int j = i;
|
||||
|
||||
if (y > i) {
|
||||
j = y;
|
||||
}
|
||||
|
||||
while (j > 0 && this.getBlockData(x, j - 1, z).c() == 0) { // OBFHELPER: getLightOpacity
|
||||
--j;
|
||||
}
|
||||
|
||||
if (j != i) {
|
||||
this.markBlocksDirtyVerticalAsync(x + this.locX * 16, z + this.locZ * 16, j, i);
|
||||
this.heightMap[z << 4 | x] = j;
|
||||
int k = this.locX * 16 + x;
|
||||
int l = this.locZ * 16 + z;
|
||||
|
||||
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
|
||||
if (j < i) {
|
||||
for (int j1 = j; j1 < i; ++j1) {
|
||||
ChunkSection extendedblockstorage2 = this.sections[j1 >> 4];
|
||||
|
||||
if (extendedblockstorage2 != Chunk.EMPTY_CHUNK_SECTION) {
|
||||
extendedblockstorage2.a(x, j1 & 15, z, 15); // OBFHELPER: setSkyLight
|
||||
// this.world.m(new BlockPosition((this.locX << 4) + x, j1, (this.locZ << 4) + z)); // OBFHELPER: notifyLightSet - client side
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i1 = i; i1 < j; ++i1) {
|
||||
ChunkSection extendedblockstorage = this.sections[i1 >> 4];
|
||||
|
||||
if (extendedblockstorage != Chunk.EMPTY_CHUNK_SECTION) {
|
||||
extendedblockstorage.a(x, i1 & 15, z, 0); // OBFHELPER: setSkyLight
|
||||
// this.world.m(new BlockPosition((this.locX << 4) + x, i1, (this.locZ << 4) + z)); // OBFHELPER: notifyLightSet - client side
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int k1 = 15;
|
||||
|
||||
while (j > 0 && k1 > 0) {
|
||||
--j;
|
||||
int i2 = this.getBlockData(x, j, z).c();
|
||||
|
||||
if (i2 == 0) {
|
||||
i2 = 1;
|
||||
}
|
||||
|
||||
k1 -= i2;
|
||||
|
||||
if (k1 < 0) {
|
||||
k1 = 0;
|
||||
}
|
||||
|
||||
ChunkSection extendedblockstorage1 = this.sections[j >> 4];
|
||||
|
||||
if (extendedblockstorage1 != Chunk.EMPTY_CHUNK_SECTION) {
|
||||
extendedblockstorage1.a(x, j & 15, z, k1); // OBFHELPER: setSkyLight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int l1 = this.heightMap[z << 4 | x];
|
||||
int j2 = i;
|
||||
int k2 = l1;
|
||||
|
||||
if (l1 < i) {
|
||||
j2 = l1;
|
||||
k2 = i;
|
||||
}
|
||||
|
||||
if (l1 < this.heightMapMinimum) {
|
||||
this.heightMapMinimum = l1;
|
||||
}
|
||||
|
||||
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
|
||||
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
|
||||
this.updateSkylightNeighborHeight(k + enumfacing.getAdjacentX(), l + enumfacing.getAdjacentZ(), j2, k2); // OBFHELPER: updateSkylightNeighborHeight
|
||||
}
|
||||
|
||||
this.updateSkylightNeighborHeight(k, l, j2, k2);
|
||||
}
|
||||
|
||||
this.markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a vertical line of blocks as dirty async.
|
||||
* Instead of calling world directly, we pass chunk safely for async light method.
|
||||
*
|
||||
* @param x1
|
||||
* @param z1
|
||||
* @param x2
|
||||
* @param z2
|
||||
*/
|
||||
private void markBlocksDirtyVerticalAsync(int x1, int z1, int x2, int z2) {
|
||||
if (x2 > z2) {
|
||||
int i = z2;
|
||||
z2 = x2;
|
||||
x2 = i;
|
||||
}
|
||||
|
||||
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
|
||||
for (int j = x2; j <= z2; ++j) {
|
||||
final BlockPosition pos = new BlockPosition(x1, j, z1);
|
||||
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, null);
|
||||
if (chunk == null) {
|
||||
continue;
|
||||
}
|
||||
((IMixinWorldServer) this.world).updateLightAsync(EnumSkyBlock.SKY, new BlockPosition(x1, j, z1), chunk);
|
||||
}
|
||||
}
|
||||
|
||||
this.world.b(x1, x2, z1, x1, z2, z1); // OBFHELPER: markBlockRangeForRenderUpdate
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks world light thread-safe.
|
||||
*
|
||||
* @param lightType The type of light to check
|
||||
* @param pos The block position
|
||||
* @return True if light update was successful, false if not
|
||||
*/
|
||||
private boolean checkWorldLightFor(EnumSkyBlock lightType, BlockPosition pos) {
|
||||
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, null);
|
||||
if (chunk == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((IMixinWorldServer) this.world).updateLightAsync(lightType, pos, chunk);
|
||||
}
|
||||
|
||||
private boolean checkWorldLight(BlockPosition pos) {
|
||||
return this.checkWorldLight(pos, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks world light async.
|
||||
*
|
||||
* @param pos The block position
|
||||
* @param neighbors A thread-safe list of surrounding neighbor chunks
|
||||
* @return True if light update was successful, false if not
|
||||
*/
|
||||
private boolean checkWorldLight(BlockPosition pos, List<Chunk> neighbors) {
|
||||
boolean flag = false;
|
||||
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, neighbors);
|
||||
if (chunk == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
|
||||
flag |= ((IMixinWorldServer) this.world).updateLightAsync(EnumSkyBlock.SKY, pos, chunk);
|
||||
}
|
||||
|
||||
flag = flag | ((IMixinWorldServer) this.world).updateLightAsync(EnumSkyBlock.BLOCK, pos, chunk);
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of block positions currently queued for lighting updates.
|
||||
*
|
||||
* @param type The light type
|
||||
* @return The list of queued block positions, empty if none
|
||||
*/
|
||||
@Override
|
||||
public CopyOnWriteArrayList<Short> getQueuedLightingUpdates(EnumSkyBlock type) {
|
||||
if (type == EnumSkyBlock.SKY) {
|
||||
return this.queuedSkyLightingUpdates;
|
||||
}
|
||||
return this.queuedBlockLightingUpdates;
|
||||
}
|
||||
}
|
||||
@@ -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.lighting;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import io.akarin.api.internal.mixin.IMixinChunk;
|
||||
import net.minecraft.server.ChunkProviderServer;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
@Mixin(value = ChunkProviderServer.class, remap = false, priority = 1001)
|
||||
public abstract class MixinChunkProviderServer {
|
||||
@Shadow @Final public WorldServer world;
|
||||
|
||||
@Redirect(method = "unloadChunks", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/Chunk;isUnloading()Z"
|
||||
))
|
||||
public boolean shouldUnload(IMixinChunk chunk) {
|
||||
if (chunk.getPendingLightUpdates().get() > 0 || this.world.getTime() - chunk.getLightUpdateTime() < 20) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +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.lighting;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.EnumSkyBlock;
|
||||
import net.minecraft.server.IChunkProvider;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
@Mixin(value = World.class, remap = false, priority = 1002)
|
||||
public abstract class MixinWorld {
|
||||
@Shadow protected IChunkProvider chunkProvider;
|
||||
@Shadow int[] J; // OBFHELPER: lightUpdateBlockList
|
||||
|
||||
@Shadow(aliases = "c") public abstract boolean checkLightFor(EnumSkyBlock lightType, BlockPosition pos);
|
||||
@Shadow public abstract MinecraftServer getMinecraftServer();
|
||||
@Shadow public abstract boolean areChunksLoaded(BlockPosition center, int radius, boolean allowEmpty);
|
||||
@Shadow public abstract Chunk getChunkIfLoaded(int x, int z);
|
||||
}
|
||||
@@ -1,375 +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.lighting;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import io.akarin.api.internal.Akari;
|
||||
import io.akarin.api.internal.mixin.IMixinChunk;
|
||||
import io.akarin.api.internal.mixin.IMixinWorldServer;
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.EnumDirection;
|
||||
import net.minecraft.server.EnumSkyBlock;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.MCUtil;
|
||||
import net.minecraft.server.MathHelper;
|
||||
import net.minecraft.server.WorldServer;
|
||||
import net.minecraft.server.BlockPosition.PooledBlockPosition;
|
||||
|
||||
@Mixin(value = WorldServer.class, remap = false, priority = 1002)
|
||||
public abstract class MixinWorldServer extends MixinWorld implements IMixinWorldServer {
|
||||
|
||||
private static final int NUM_XZ_BITS = 4;
|
||||
private static final int NUM_SHORT_Y_BITS = 8;
|
||||
private static final short XZ_MASK = 0xF;
|
||||
private static final short Y_SHORT_MASK = 0xFF;
|
||||
|
||||
private final static ExecutorService lightExecutorService = getExecutorService();
|
||||
private static ExecutorService getExecutorService() {
|
||||
return AkarinGlobalConfig.asyncLightingWorkStealing ?
|
||||
Executors.newFixedThreadPool(AkarinGlobalConfig.asyncLightingThreads, new ThreadFactoryBuilder().setNameFormat("Akarin Async Light Thread").build())
|
||||
:
|
||||
Executors.newWorkStealingPool(AkarinGlobalConfig.asyncLightingThreads);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkLightFor(EnumSkyBlock lightType, BlockPosition pos) { // OBFHELPER: checkLightFor
|
||||
return updateLightAsync(lightType, pos, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
|
||||
// Sponge - This check is not needed as neighbors are checked in updateLightAsync
|
||||
if (false && !this.areChunksLoaded(pos, 17, false)) {
|
||||
return false;
|
||||
} else {
|
||||
final IMixinChunk spongeChunk = (IMixinChunk) currentChunk;
|
||||
int recheckIndex = 0;
|
||||
int blockIndex = 0;
|
||||
int current = this.getLightForAsync(lightType, pos, currentChunk, neighbors); // Sponge - use thread safe method
|
||||
int rawLight = this.getRawBlockLightAsync(lightType, pos, currentChunk, neighbors); // Sponge - use thread safe method
|
||||
int x = pos.getX();
|
||||
int y = pos.getY();
|
||||
int z = pos.getZ();
|
||||
|
||||
if (rawLight > current) {
|
||||
this.J[blockIndex++] = 133152; // OBFHELPER: lightUpdateBlockList
|
||||
} else if (rawLight < current) {
|
||||
this.J[blockIndex++] = 133152 | current << 18; // OBFHELPER: lightUpdateBlockList
|
||||
|
||||
while (recheckIndex < blockIndex) {
|
||||
int blockData = this.J[recheckIndex++]; // OBFHELPER: lightUpdateBlockList
|
||||
int i2 = (blockData & 63) - 32 + x;
|
||||
int j2 = (blockData >> 6 & 63) - 32 + y;
|
||||
int k2 = (blockData >> 12 & 63) - 32 + z;
|
||||
int l2 = blockData >> 18 & 15;
|
||||
BlockPosition blockpos = new BlockPosition(i2, j2, k2);
|
||||
int lightLevel = this.getLightForAsync(lightType, blockpos, currentChunk, neighbors); // Sponge - use thread safe method
|
||||
|
||||
if (lightLevel == l2) {
|
||||
this.setLightForAsync(lightType, blockpos, 0, currentChunk, neighbors); // Sponge - use thread safe method
|
||||
|
||||
if (l2 > 0) {
|
||||
int j3 = MathHelper.a(i2 - x); // abs
|
||||
int k3 = MathHelper.a(j2 - y);
|
||||
int l3 = MathHelper.a(k2 - z);
|
||||
|
||||
if (j3 + k3 + l3 < 17) {
|
||||
PooledBlockPosition mutableBlockpos = PooledBlockPosition.aquire();
|
||||
|
||||
for (EnumDirection enumfacing : EnumDirection.values()) {
|
||||
int i4 = i2 + enumfacing.getAdjacentX();
|
||||
int j4 = j2 + enumfacing.getAdjacentX();
|
||||
int k4 = k2 + enumfacing.getAdjacentX();
|
||||
mutableBlockpos.setValues(i4, j4, k4);
|
||||
// Sponge start - get chunk safely
|
||||
final Chunk pooledChunk = this.getLightChunk(mutableBlockpos, currentChunk, neighbors);
|
||||
if (pooledChunk == null) {
|
||||
continue;
|
||||
}
|
||||
int opacity = Math.max(1, pooledChunk.getBlockData(mutableBlockpos).c()); // OBFHELPER: getLightOpacity
|
||||
lightLevel = this.getLightForAsync(lightType, mutableBlockpos, currentChunk, neighbors);
|
||||
// Sponge end
|
||||
|
||||
if (lightLevel == l2 - opacity && blockIndex < this.J.length) { // OBFHELPER: lightUpdateBlockList
|
||||
this.J[blockIndex++] = i4 - x + 32 | j4 - y + 32 << 6 | k4 - z + 32 << 12 | l2 - opacity << 18; // OBFHELPER: lightUpdateBlockList
|
||||
}
|
||||
}
|
||||
|
||||
mutableBlockpos.free();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recheckIndex = 0;
|
||||
}
|
||||
|
||||
while (recheckIndex < blockIndex) {
|
||||
int i5 = this.J[recheckIndex++]; // OBFHELPER: lightUpdateBlockList
|
||||
int j5 = (i5 & 63) - 32 + x;
|
||||
int k5 = (i5 >> 6 & 63) - 32 + y;
|
||||
int l5 = (i5 >> 12 & 63) - 32 + z;
|
||||
BlockPosition blockpos1 = new BlockPosition(j5, k5, l5);
|
||||
int i6 = this.getLightForAsync(lightType, blockpos1, currentChunk, neighbors); // Sponge - use thread safe method
|
||||
int j6 = this.getRawBlockLightAsync(lightType, blockpos1, currentChunk, neighbors); // Sponge - use thread safe method
|
||||
|
||||
if (j6 != i6) {
|
||||
this.setLightForAsync(lightType, blockpos1, j6, currentChunk, neighbors); // Sponge - use thread safe method
|
||||
|
||||
if (j6 > i6) {
|
||||
int k6 = Math.abs(j5 - x);
|
||||
int l6 = Math.abs(k5 - y);
|
||||
int i7 = Math.abs(l5 - z);
|
||||
boolean flag = blockIndex < this.J.length - 6; // OBFHELPER: lightUpdateBlockList
|
||||
|
||||
if (k6 + l6 + i7 < 17 && flag) {
|
||||
// Sponge start - use thread safe method getLightForAsync
|
||||
if (this.getLightForAsync(lightType, blockpos1.west(), currentChunk, neighbors) < j6) {
|
||||
this.J[blockIndex++] = j5 - 1 - x + 32 + (k5 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
|
||||
}
|
||||
|
||||
if (this.getLightForAsync(lightType, blockpos1.east(), currentChunk, neighbors) < j6) {
|
||||
this.J[blockIndex++] = j5 + 1 - x + 32 + (k5 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
|
||||
}
|
||||
|
||||
if (this.getLightForAsync(lightType, blockpos1.down(), currentChunk, neighbors) < j6) {
|
||||
this.J[blockIndex++] = j5 - x + 32 + (k5 - 1 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
|
||||
}
|
||||
|
||||
if (this.getLightForAsync(lightType, blockpos1.up(), currentChunk, neighbors) < j6) {
|
||||
this.J[blockIndex++] = j5 - x + 32 + (k5 + 1 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
|
||||
}
|
||||
|
||||
if (this.getLightForAsync(lightType, blockpos1.north(), currentChunk, neighbors) < j6) {
|
||||
this.J[blockIndex++] = j5 - x + 32 + (k5 - y + 32 << 6) + (l5 - 1 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
|
||||
}
|
||||
|
||||
if (this.getLightForAsync(lightType, blockpos1.south(), currentChunk, neighbors) < j6) {
|
||||
this.J[blockIndex++] = j5 - x + 32 + (k5 - y + 32 << 6) + (l5 + 1 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
|
||||
}
|
||||
// Sponge end
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sponge start - Asynchronous light updates
|
||||
spongeChunk.getQueuedLightingUpdates(lightType).remove((Short) this.blockPosToShort(pos));
|
||||
spongeChunk.getPendingLightUpdates().decrementAndGet();
|
||||
for (Chunk neighborChunk : neighbors) {
|
||||
final IMixinChunk neighbor = (IMixinChunk) neighborChunk;
|
||||
neighbor.getPendingLightUpdates().decrementAndGet();
|
||||
}
|
||||
// Sponge end
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateLightAsync(EnumSkyBlock lightType, BlockPosition pos, @Nullable Chunk currentChunk) {
|
||||
if (this.getMinecraftServer().isStopped() || lightExecutorService.isShutdown()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentChunk == null) {
|
||||
currentChunk = MCUtil.getLoadedChunkWithoutMarkingActive(chunkProvider, pos.getX() >> 4, pos.getZ() >> 4);
|
||||
}
|
||||
|
||||
final IMixinChunk spongeChunk = (IMixinChunk) currentChunk;
|
||||
if (currentChunk == null || currentChunk.isUnloading() || !spongeChunk.areNeighborsLoaded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final short shortPos = this.blockPosToShort(pos);
|
||||
if (spongeChunk.getQueuedLightingUpdates(lightType).contains(shortPos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Chunk chunk = currentChunk;
|
||||
spongeChunk.getQueuedLightingUpdates(lightType).add(shortPos);
|
||||
spongeChunk.getPendingLightUpdates().incrementAndGet();
|
||||
spongeChunk.setLightUpdateTime(chunk.getWorld().getTime());
|
||||
|
||||
List<Chunk> neighbors = spongeChunk.getNeighbors();
|
||||
// add diagonal chunks
|
||||
Chunk southEastChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(0)).getNeighborChunk(2);
|
||||
Chunk southWestChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(0)).getNeighborChunk(3);
|
||||
Chunk northEastChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(1)).getNeighborChunk(2);
|
||||
Chunk northWestChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(1)).getNeighborChunk(3);
|
||||
if (southEastChunk != null) {
|
||||
neighbors.add(southEastChunk);
|
||||
}
|
||||
if (southWestChunk != null) {
|
||||
neighbors.add(southWestChunk);
|
||||
}
|
||||
if (northEastChunk != null) {
|
||||
neighbors.add(northEastChunk);
|
||||
}
|
||||
if (northWestChunk != null) {
|
||||
neighbors.add(northWestChunk);
|
||||
}
|
||||
|
||||
for (Chunk neighborChunk : neighbors) {
|
||||
final IMixinChunk neighbor = (IMixinChunk) neighborChunk;
|
||||
neighbor.getPendingLightUpdates().incrementAndGet();
|
||||
neighbor.setLightUpdateTime(chunk.getWorld().getTime());
|
||||
}
|
||||
|
||||
if (Akari.isPrimaryThread()) { // Akarin
|
||||
lightExecutorService.execute(() -> {
|
||||
this.checkLightAsync(lightType, pos, chunk, neighbors);
|
||||
});
|
||||
} else {
|
||||
this.checkLightAsync(lightType, pos, chunk, neighbors);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutorService getLightingExecutor() {
|
||||
return lightExecutorService;
|
||||
}
|
||||
|
||||
// Thread safe methods to retrieve a chunk during async light updates
|
||||
// Each method avoids calling getLoadedChunk and instead accesses the passed neighbor chunk list to avoid concurrency issues
|
||||
public Chunk getLightChunk(BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
|
||||
if (currentChunk.a(pos.getX() >> 4, pos.getZ() >> 4)) { // OBFHELPER: isAtLocation
|
||||
if (currentChunk.isUnloading()) {
|
||||
return null;
|
||||
}
|
||||
return currentChunk;
|
||||
}
|
||||
for (Chunk neighbor : neighbors) {
|
||||
if (neighbor.a(pos.getX() >> 4, pos.getZ() >> 4)) { // OBFHELPER: isAtLocation
|
||||
if (neighbor.isUnloading()) {
|
||||
return null;
|
||||
}
|
||||
return neighbor;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private int getLightForAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
|
||||
if (pos.getY() < 0) {
|
||||
pos = new BlockPosition(pos.getX(), 0, pos.getZ());
|
||||
}
|
||||
if (!pos.isValidLocation()) {
|
||||
return lightType.c; // OBFHELPER: defaultLightValue
|
||||
}
|
||||
|
||||
final Chunk chunk = this.getLightChunk(pos, currentChunk, neighbors);
|
||||
if (chunk == null || chunk.isUnloading()) {
|
||||
return lightType.c; // OBFHELPER: defaultLightValue
|
||||
}
|
||||
|
||||
return chunk.getBrightness(lightType, pos);
|
||||
}
|
||||
|
||||
private int getRawBlockLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
|
||||
final Chunk chunk = getLightChunk(pos, currentChunk, neighbors);
|
||||
if (chunk == null || chunk.isUnloading()) {
|
||||
return lightType.c; // OBFHELPER: defaultLightValue
|
||||
}
|
||||
if (lightType == EnumSkyBlock.SKY && chunk.c(pos)) { // OBFHELPER: canSeeSky
|
||||
return 15;
|
||||
} else {
|
||||
IBlockData blockData = chunk.getBlockData(pos);
|
||||
int blockLight = blockData.d(); // getLightValue
|
||||
int rawLight = lightType == EnumSkyBlock.SKY ? 0 : blockLight;
|
||||
int opacity = blockData.c(); // OBFHELPER: getLightOpacity
|
||||
|
||||
if (opacity >= 15 && blockLight > 0) {
|
||||
opacity = 1;
|
||||
}
|
||||
|
||||
if (opacity < 1) {
|
||||
opacity = 1;
|
||||
}
|
||||
|
||||
if (opacity >= 15) {
|
||||
return 0;
|
||||
} else if (rawLight >= 14) {
|
||||
return rawLight;
|
||||
} else {
|
||||
for (EnumDirection facing : EnumDirection.values()) {
|
||||
BlockPosition blockpos = pos.shift(facing);
|
||||
int current = this.getLightForAsync(lightType, blockpos, currentChunk, neighbors) - opacity;
|
||||
|
||||
if (current > rawLight) {
|
||||
rawLight = current;
|
||||
}
|
||||
|
||||
if (rawLight >= 14) {
|
||||
return rawLight;
|
||||
}
|
||||
}
|
||||
|
||||
return rawLight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setLightForAsync(EnumSkyBlock type, BlockPosition pos, int lightValue, Chunk currentChunk, List<Chunk> neighbors) {
|
||||
if (pos.isValidLocation()) {
|
||||
final Chunk chunk = this.getLightChunk(pos, currentChunk, neighbors);
|
||||
if (chunk != null && !chunk.isUnloading()) {
|
||||
chunk.a(type, pos, lightValue); // OBFHELPER: setLightFor
|
||||
// this.notifyLightSet(pos); // client side
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private short blockPosToShort(BlockPosition pos) {
|
||||
short serialized = (short) setNibble(0, pos.getX() & XZ_MASK, 0, NUM_XZ_BITS);
|
||||
serialized = (short) setNibble(serialized, pos.getY() & Y_SHORT_MASK, 1, NUM_SHORT_Y_BITS);
|
||||
serialized = (short) setNibble(serialized, pos.getZ() & XZ_MASK, 3, NUM_XZ_BITS);
|
||||
return serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies bits in an integer.
|
||||
*
|
||||
* @param num Integer to modify
|
||||
* @param data Bits of data to add
|
||||
* @param which Index of nibble to start at
|
||||
* @param bitsToReplace The number of bits to replace starting from nibble index
|
||||
* @return The modified integer
|
||||
*/
|
||||
private int setNibble(int num, int data, int which, int bitsToReplace) {
|
||||
return (num & ~(bitsToReplace << (which * 4)) | (data << (which * 4)));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public abstract class MixinEntity {
|
||||
private int lastLavaCheck = Integer.MIN_VALUE;
|
||||
|
||||
@Overwrite // OBFHELPER: isInLava
|
||||
public boolean au() {
|
||||
public boolean ax() {
|
||||
/*
|
||||
* This originally comes from Migot (https://github.com/Poweruser/Migot/commit/cafbf1707107d2a3aa6232879f305975bb1f0285)
|
||||
* Thanks @Poweruser
|
||||
@@ -27,7 +27,7 @@ public abstract class MixinEntity {
|
||||
int currentTick = MinecraftServer.currentTick;
|
||||
if (this.lastLavaCheck != currentTick) {
|
||||
this.lastLavaCheck = currentTick;
|
||||
this.isInLava = this.world.a(this.getBoundingBox().grow(-0.10000000149011612D, -0.4000000059604645D, -0.10000000149011612D), Material.LAVA);
|
||||
this.isInLava = this.world.a(this.getBoundingBox().f(0.10000000149011612D, 0.4000000059604645D, 0.10000000149011612D), Material.LAVA);
|
||||
}
|
||||
return this.isInLava;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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,508 +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);
|
||||
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);
|
||||
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
@@ -0,0 +1,500 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nullable;
|
||||
import com.destroystokyo.paper.exception.ServerInternalException;
|
||||
|
||||
import io.akarin.api.internal.Akari;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
|
||||
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Lock for event (safety issue)
|
||||
*/
|
||||
public class ChunkProviderServer implements IChunkProvider {
|
||||
|
||||
private static final Logger a = LogManager.getLogger();
|
||||
public final LongSet unloadQueue = new LongOpenHashSet();
|
||||
public final ChunkGenerator<?> chunkGenerator;
|
||||
public final IChunkLoader chunkLoader; // PAIL
|
||||
// Paper start - chunk save stats
|
||||
private long lastQueuedSaves = 0L; // Paper
|
||||
private long lastProcessedSaves = 0L; // Paper
|
||||
private long lastSaveStatPrinted = System.currentTimeMillis();
|
||||
// Paper end
|
||||
public final Long2ObjectMap<Chunk> chunks = Long2ObjectMaps.synchronize(new ChunkMap(8192));
|
||||
private Chunk lastChunk;
|
||||
private final ChunkTaskScheduler chunkScheduler;
|
||||
final SchedulerBatch<ChunkCoordIntPair, ChunkStatus, ProtoChunk> batchScheduler; // Paper
|
||||
public final WorldServer world;
|
||||
final IAsyncTaskHandler asyncTaskHandler; // Paper
|
||||
|
||||
public ChunkProviderServer(WorldServer worldserver, IChunkLoader ichunkloader, ChunkGenerator<?> chunkgenerator, IAsyncTaskHandler iasynctaskhandler) {
|
||||
this.world = worldserver;
|
||||
this.chunkLoader = ichunkloader;
|
||||
this.chunkGenerator = chunkgenerator;
|
||||
this.asyncTaskHandler = iasynctaskhandler;
|
||||
this.chunkScheduler = new ChunkTaskScheduler(0, worldserver, chunkgenerator, ichunkloader, iasynctaskhandler); // CraftBukkit - very buggy, broken in lots of __subtle__ ways. Same goes for async chunk loading. Also Bukkit API / plugins can't handle async events at all anyway.
|
||||
this.batchScheduler = new SchedulerBatch(this.chunkScheduler);
|
||||
}
|
||||
|
||||
public Collection<Chunk> a() {
|
||||
return this.chunks.values();
|
||||
}
|
||||
|
||||
public void unload(Chunk chunk) {
|
||||
if (this.world.worldProvider.a(chunk.locX, chunk.locZ)) {
|
||||
this.unloadQueue.add(ChunkCoordIntPair.a(chunk.locX, chunk.locZ));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void b() {
|
||||
ObjectIterator objectiterator = this.chunks.values().iterator();
|
||||
|
||||
while (objectiterator.hasNext()) {
|
||||
Chunk chunk = (Chunk) objectiterator.next();
|
||||
|
||||
this.unload(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(int i, int j) {
|
||||
this.unloadQueue.remove(ChunkCoordIntPair.a(i, j));
|
||||
}
|
||||
|
||||
// Paper start - defaults if Async Chunks is not enabled
|
||||
boolean chunkGoingToExists(int x, int z) {
|
||||
final long k = ChunkCoordIntPair.asLong(x, z);
|
||||
return chunkScheduler.progressCache.containsKey(k);
|
||||
}
|
||||
public void bumpPriority(ChunkCoordIntPair coords) {
|
||||
// do nothing, override in async
|
||||
}
|
||||
|
||||
public List<ChunkCoordIntPair> getSpiralOutChunks(BlockPosition blockposition, int radius) {
|
||||
List<ChunkCoordIntPair> list = com.google.common.collect.Lists.newArrayList();
|
||||
|
||||
for (int r = 1; r <= radius; r++) {
|
||||
int x = -r;
|
||||
int z = r;
|
||||
list.add(new ChunkCoordIntPair(blockposition.getX(), blockposition.getZ()));
|
||||
// Iterates the edge of half of the box; then negates for other half.
|
||||
while (x <= r && z > -r) {
|
||||
list.add(new ChunkCoordIntPair(blockposition.getX() + x, blockposition.getZ() + z));
|
||||
list.add(new ChunkCoordIntPair(blockposition.getX() - x, blockposition.getZ() - z));
|
||||
|
||||
if (x < r) {
|
||||
x++;
|
||||
} else {
|
||||
z--;
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public Chunk getChunkAt(int x, int z, boolean load, boolean gen, Consumer<Chunk> consumer) {
|
||||
return getChunkAt(x, z, load, gen, false, consumer);
|
||||
}
|
||||
public Chunk getChunkAt(int x, int z, boolean load, boolean gen, boolean priority, Consumer<Chunk> consumer) {
|
||||
Chunk chunk = getChunkAt(x, z, load, gen);
|
||||
if (consumer != null) {
|
||||
consumer.accept(chunk);
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
@Nullable
|
||||
public Chunk getChunkAt(int i, int j, boolean flag, boolean flag1) {
|
||||
IChunkLoader ichunkloader = this.chunkLoader;
|
||||
Chunk chunk;
|
||||
// Paper start - do already loaded checks before synchronize
|
||||
long k = ChunkCoordIntPair.a(i, j);
|
||||
chunk = (Chunk) this.chunks.get(k);
|
||||
if (chunk != null) {
|
||||
//this.lastChunk = chunk; // Paper remove vanilla lastChunk
|
||||
return chunk;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
synchronized (this.chunkLoader) {
|
||||
// Paper start - remove vanilla lastChunk, we do it more accurately
|
||||
/* if (this.lastChunk != null && this.lastChunk.locX == i && this.lastChunk.locZ == j) {
|
||||
return this.lastChunk;
|
||||
}*/ // Paper end
|
||||
|
||||
// Paper start - move up
|
||||
//long k = ChunkCoordIntPair.a(i, j);
|
||||
|
||||
/*chunk = (Chunk) this.chunks.get(k);
|
||||
if (chunk != null) {
|
||||
//this.lastChunk = chunk; // Paper remove vanilla lastChunk
|
||||
return chunk;
|
||||
}*/
|
||||
// Paper end
|
||||
|
||||
if (flag) {
|
||||
try (co.aikar.timings.Timing timing = world.timings.syncChunkLoadTimer.startTiming()) { // Paper
|
||||
// CraftBukkit - decompile error
|
||||
chunk = this.chunkLoader.a(this.world, i, j, (chunk1) -> {
|
||||
chunk1.setLastSaved(this.world.getTime());
|
||||
this.chunks.put(ChunkCoordIntPair.a(i, j), chunk1);
|
||||
});
|
||||
} catch (Exception exception) {
|
||||
ChunkProviderServer.a.error("Couldn\'t load chunk", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (chunk != null) {
|
||||
this.asyncTaskHandler.postToMainThread(chunk::addEntities);
|
||||
return chunk;
|
||||
} else if (flag1) {
|
||||
try (co.aikar.timings.Timing timing = world.timings.chunkGeneration.startTiming()) { // Paper
|
||||
this.batchScheduler.b();
|
||||
this.batchScheduler.a(new ChunkCoordIntPair(i, j));
|
||||
CompletableFuture<ProtoChunk> completablefuture = this.batchScheduler.c(); // CraftBukkit - decompile error
|
||||
|
||||
return (Chunk) completablefuture.thenApply(this::a).join();
|
||||
} catch (RuntimeException runtimeexception) {
|
||||
throw this.a(i, j, (Throwable) runtimeexception);
|
||||
}
|
||||
// finally { world.timings.syncChunkLoadTimer.stopTiming(); } // Spigot // Paper
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
public Chunk generateChunk(int x, int z) {
|
||||
try {
|
||||
this.batchScheduler.b();
|
||||
ChunkCoordIntPair pos = new ChunkCoordIntPair(x, z);
|
||||
this.chunkScheduler.forcePolluteCache(pos);
|
||||
this.batchScheduler.a(pos);
|
||||
CompletableFuture<ProtoChunk> completablefuture = this.batchScheduler.c();
|
||||
|
||||
return (Chunk) completablefuture.thenApply(this::a).join();
|
||||
} catch (RuntimeException runtimeexception) {
|
||||
throw this.a(x, z, (Throwable) runtimeexception);
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
public IChunkAccess a(int i, int j, boolean flag) {
|
||||
Chunk chunk = this.getChunkAt(i, j, true, false);
|
||||
|
||||
return (IChunkAccess) (chunk != null ? chunk : (IChunkAccess) this.chunkScheduler.b(new ChunkCoordIntPair(i, j), flag));
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> loadAllChunks(Iterable<ChunkCoordIntPair> iterable, Consumer<Chunk> consumer) { return a(iterable, consumer).thenCompose(protoChunk -> null); } // Paper - overriden in async chunk provider
|
||||
private CompletableFuture<ProtoChunk> a(Iterable<ChunkCoordIntPair> iterable, Consumer<Chunk> consumer) { // Paper - mark private, use above method
|
||||
this.batchScheduler.b();
|
||||
Iterator iterator = iterable.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) iterator.next();
|
||||
Chunk chunk = this.getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z, true, false);
|
||||
|
||||
if (chunk != null) {
|
||||
consumer.accept(chunk);
|
||||
} else {
|
||||
this.batchScheduler.a(chunkcoordintpair).thenApply(this::a).thenAccept(consumer);
|
||||
}
|
||||
}
|
||||
|
||||
return this.batchScheduler.c();
|
||||
}
|
||||
|
||||
ReportedException generateChunkError(int i, int j, Throwable throwable) { return a(i, j, throwable); } // Paper - OBFHELPER
|
||||
private ReportedException a(int i, int j, Throwable throwable) {
|
||||
CrashReport crashreport = CrashReport.a(throwable, "Exception generating new chunk");
|
||||
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Chunk to be generated");
|
||||
|
||||
crashreportsystemdetails.a("Location", (Object) String.format("%d,%d", new Object[] { Integer.valueOf(i), Integer.valueOf(j)}));
|
||||
crashreportsystemdetails.a("Position hash", (Object) Long.valueOf(ChunkCoordIntPair.a(i, j)));
|
||||
crashreportsystemdetails.a("Generator", (Object) this.chunkGenerator);
|
||||
return new ReportedException(crashreport);
|
||||
}
|
||||
|
||||
private Chunk a(IChunkAccess ichunkaccess) {
|
||||
ChunkCoordIntPair chunkcoordintpair = ichunkaccess.getPos();
|
||||
int i = chunkcoordintpair.x;
|
||||
int j = chunkcoordintpair.z;
|
||||
long k = ChunkCoordIntPair.a(i, j);
|
||||
Long2ObjectMap long2objectmap = this.chunks;
|
||||
Chunk chunk;
|
||||
|
||||
Akari.eventLock.lock(); // Akarin
|
||||
try { // Akarin
|
||||
synchronized (this.chunks) {
|
||||
Chunk chunk1 = (Chunk) this.chunks.get(k);
|
||||
|
||||
if (chunk1 != null) {
|
||||
return chunk1;
|
||||
}
|
||||
|
||||
if (ichunkaccess instanceof Chunk) {
|
||||
chunk = (Chunk) ichunkaccess;
|
||||
} else {
|
||||
if (!(ichunkaccess instanceof ProtoChunk)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
chunk = new Chunk(this.world, (ProtoChunk) ichunkaccess, i, j);
|
||||
}
|
||||
|
||||
this.chunks.put(k, chunk);
|
||||
//this.lastChunk = chunk; // Paper
|
||||
}
|
||||
} finally { Akari.eventLock.unlock(); } // Akarin
|
||||
|
||||
this.asyncTaskHandler.postToMainThread(chunk::addEntities);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
public void saveChunk(IChunkAccess ichunkaccess, boolean unloaded) { // Spigot
|
||||
try (co.aikar.timings.Timing timed = world.timings.chunkSaveData.startTiming()) { // Paper - Timings
|
||||
ichunkaccess.setLastSaved(this.world.getTime());
|
||||
this.chunkLoader.saveChunk(this.world, ichunkaccess, unloaded); // Spigot
|
||||
} catch (IOException ioexception) {
|
||||
// Paper start
|
||||
String msg = "Couldn\'t save chunk";
|
||||
ChunkProviderServer.a.error(msg, ioexception);
|
||||
ServerInternalException.reportInternalException(ioexception);
|
||||
} catch (ExceptionWorldConflict exceptionworldconflict) {
|
||||
String msg = "Couldn\'t save chunk; already in use by another instance of Minecraft?";
|
||||
ChunkProviderServer.a.error(msg, exceptionworldconflict);
|
||||
ServerInternalException.reportInternalException(exceptionworldconflict);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean a(boolean flag) {
|
||||
int i = 0;
|
||||
|
||||
this.chunkScheduler.a(() -> {
|
||||
return true;
|
||||
});
|
||||
IChunkLoader ichunkloader = this.chunkLoader;
|
||||
|
||||
synchronized (this.chunkLoader) {
|
||||
ObjectIterator objectiterator = this.chunks.values().iterator();
|
||||
|
||||
// Paper start
|
||||
final ChunkRegionLoader chunkLoader = (ChunkRegionLoader) world.getChunkProviderServer().chunkLoader;
|
||||
final int queueSize = chunkLoader.getQueueSize();
|
||||
|
||||
final long now = System.currentTimeMillis();
|
||||
final long timeSince = (now - lastSaveStatPrinted) / 1000;
|
||||
final Integer printRateSecs = Integer.getInteger("printSaveStats");
|
||||
if (printRateSecs != null && timeSince >= printRateSecs) {
|
||||
final String timeStr = "/" + timeSince +"s";
|
||||
final long queuedSaves = chunkLoader.getQueuedSaves();
|
||||
long queuedDiff = queuedSaves - lastQueuedSaves;
|
||||
lastQueuedSaves = queuedSaves;
|
||||
|
||||
final long processedSaves = chunkLoader.getProcessedSaves();
|
||||
long processedDiff = processedSaves - lastProcessedSaves;
|
||||
lastProcessedSaves = processedSaves;
|
||||
|
||||
lastSaveStatPrinted = now;
|
||||
if (processedDiff > 0 || queueSize > 0 || queuedDiff > 0) {
|
||||
System.out.println("[Chunk Save Stats] " + world.worldData.getName() +
|
||||
" - Current: " + queueSize +
|
||||
" - Queued: " + queuedDiff + timeStr +
|
||||
" - Processed: " +processedDiff + timeStr
|
||||
);
|
||||
}
|
||||
}
|
||||
if (queueSize > world.paperConfig.queueSizeAutoSaveThreshold){
|
||||
return false;
|
||||
}
|
||||
// Paper end
|
||||
while (objectiterator.hasNext()) {
|
||||
Chunk chunk = (Chunk) objectiterator.next();
|
||||
|
||||
if (chunk.c(flag)) {
|
||||
this.saveChunk(chunk, false); // Spigot
|
||||
chunk.a(false);
|
||||
++i;
|
||||
if (!flag && i >= world.paperConfig.maxAutoSaveChunksPerTick) { // Spigot - // Paper - Incremental Auto Save - cap max
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
// Paper start - we do not need to wait for chunk generations to finish on close
|
||||
/*try {
|
||||
this.batchScheduler.a();
|
||||
} catch (InterruptedException interruptedexception) {
|
||||
ChunkProviderServer.a.error("Couldn\'t stop taskManager", interruptedexception);
|
||||
}*/
|
||||
// Paper end
|
||||
|
||||
}
|
||||
|
||||
public void c() {
|
||||
IChunkLoader ichunkloader = this.chunkLoader;
|
||||
|
||||
synchronized (this.chunkLoader) {
|
||||
this.chunkLoader.b();
|
||||
}
|
||||
}
|
||||
|
||||
private static final double UNLOAD_QUEUE_RESIZE_FACTOR = 0.96; // Spigot
|
||||
|
||||
public boolean unloadChunks(BooleanSupplier booleansupplier) {
|
||||
if (!this.world.savingDisabled) {
|
||||
if (!this.unloadQueue.isEmpty()) {
|
||||
// Spigot start
|
||||
org.spigotmc.SlackActivityAccountant activityAccountant = this.world.getMinecraftServer().slackActivityAccountant;
|
||||
activityAccountant.startActivity(0.5);
|
||||
int targetSize = Math.min(this.unloadQueue.size() - 100, (int) (this.unloadQueue.size() * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive
|
||||
// Spigot end
|
||||
|
||||
LongIterator longiterator = this.unloadQueue.iterator();
|
||||
|
||||
while (longiterator.hasNext()) { // Spigot
|
||||
Long olong = (Long) longiterator.next();
|
||||
longiterator.remove(); // Spigot
|
||||
IChunkLoader ichunkloader = this.chunkLoader;
|
||||
|
||||
synchronized (this.chunkLoader) {
|
||||
Chunk chunk = (Chunk) this.chunks.get(olong);
|
||||
|
||||
if (chunk != null) {
|
||||
// CraftBukkit start - move unload logic to own method
|
||||
if (!unloadChunk(chunk, true)) {
|
||||
continue;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
// Spigot start
|
||||
if (!booleansupplier.getAsBoolean() && this.unloadQueue.size() <= targetSize && activityAccountant.activityTimeIsExhausted()) {
|
||||
break;
|
||||
}
|
||||
// Spigot end
|
||||
}
|
||||
}
|
||||
}
|
||||
activityAccountant.endActivity(); // Spigot
|
||||
}
|
||||
// Paper start - delayed chunk unloads
|
||||
long now = System.currentTimeMillis();
|
||||
long unloadAfter = world.paperConfig.delayChunkUnloadsBy;
|
||||
if (unloadAfter > 0) {
|
||||
//noinspection Convert2streamapi
|
||||
for (Chunk chunk : chunks.values()) {
|
||||
if (chunk.scheduledForUnload != null && now - chunk.scheduledForUnload > unloadAfter) {
|
||||
chunk.scheduledForUnload = null;
|
||||
unload(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
|
||||
this.chunkScheduler.a(booleansupplier);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
public boolean unloadChunk(Chunk chunk, boolean save) {
|
||||
ChunkUnloadEvent event = new ChunkUnloadEvent(chunk.bukkitChunk, save);
|
||||
this.world.getServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
save = event.isSaveChunk();
|
||||
chunk.lightingQueue.processUnload(); // Paper
|
||||
|
||||
// Update neighbor counts
|
||||
for (int x = -2; x < 3; x++) {
|
||||
for (int z = -2; z < 3; z++) {
|
||||
if (x == 0 && z == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Chunk neighbor = this.chunks.get(chunk.chunkKey); // Paper
|
||||
if (neighbor != null) {
|
||||
neighbor.setNeighborUnloaded(-x, -z);
|
||||
chunk.setNeighborUnloaded(x, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Moved from unloadChunks above
|
||||
synchronized (this.chunkLoader) {
|
||||
chunk.removeEntities();
|
||||
if (save) {
|
||||
this.saveChunk(chunk, true); // Spigot
|
||||
}
|
||||
this.chunks.remove(chunk.chunkKey);
|
||||
// this.lastChunk = null; // Paper
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
public boolean d() {
|
||||
return !this.world.savingDisabled;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "ServerChunkCache: " + this.chunks.size() + " Drop: " + this.unloadQueue.size();
|
||||
}
|
||||
|
||||
public List<BiomeBase.BiomeMeta> a(EnumCreatureType enumcreaturetype, BlockPosition blockposition) {
|
||||
return this.chunkGenerator.getMobsFor(enumcreaturetype, blockposition);
|
||||
}
|
||||
|
||||
public int a(World world, boolean flag, boolean flag1) {
|
||||
return this.chunkGenerator.a(world, flag, flag1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BlockPosition a(World world, String s, BlockPosition blockposition, int i, boolean flag) {
|
||||
return this.chunkGenerator.findNearestMapFeature(world, s, blockposition, i, flag);
|
||||
}
|
||||
|
||||
public ChunkGenerator<?> getChunkGenerator() {
|
||||
return this.chunkGenerator;
|
||||
}
|
||||
|
||||
public int g() {
|
||||
return this.chunks.size();
|
||||
}
|
||||
|
||||
public boolean isLoaded(int i, int j) {
|
||||
return this.chunks.containsKey(ChunkCoordIntPair.a(i, j));
|
||||
}
|
||||
}
|
||||
@@ -1,432 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Expose private members (cause mixin errors)
|
||||
*/
|
||||
public class EnchantmentManager {
|
||||
|
||||
private static final EnchantmentManager.EnchantmentModifierProtection a = new EnchantmentManager.EnchantmentModifierProtection(null);
|
||||
private static final EnchantmentManager.EnchantmentModifierDamage b = new EnchantmentManager.EnchantmentModifierDamage(null);
|
||||
private static final EnchantmentManager.EnchantmentModifierThorns c = new EnchantmentManager.EnchantmentModifierThorns(null);
|
||||
private static final EnchantmentManager.EnchantmentModifierArthropods d = new EnchantmentManager.EnchantmentModifierArthropods(null);
|
||||
|
||||
public static int getEnchantmentLevel(Enchantment enchantment, ItemStack itemstack) {
|
||||
if (itemstack.isEmpty()) {
|
||||
return 0;
|
||||
} else {
|
||||
NBTTagList nbttaglist = itemstack.getEnchantments();
|
||||
|
||||
for (int i = 0; i < nbttaglist.size(); ++i) {
|
||||
NBTTagCompound nbttagcompound = nbttaglist.get(i);
|
||||
Enchantment enchantment1 = Enchantment.c(nbttagcompound.getShort("id"));
|
||||
short short0 = nbttagcompound.getShort("lvl");
|
||||
|
||||
if (enchantment1 == enchantment) {
|
||||
return short0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<Enchantment, Integer> a(ItemStack itemstack) {
|
||||
LinkedHashMap linkedhashmap = Maps.newLinkedHashMap();
|
||||
NBTTagList nbttaglist = itemstack.getItem() == Items.ENCHANTED_BOOK ? ItemEnchantedBook.h(itemstack) : itemstack.getEnchantments();
|
||||
|
||||
for (int i = 0; i < nbttaglist.size(); ++i) {
|
||||
NBTTagCompound nbttagcompound = nbttaglist.get(i);
|
||||
Enchantment enchantment = Enchantment.c(nbttagcompound.getShort("id"));
|
||||
short short0 = nbttagcompound.getShort("lvl");
|
||||
|
||||
linkedhashmap.put(enchantment, Integer.valueOf(short0));
|
||||
}
|
||||
|
||||
return linkedhashmap;
|
||||
}
|
||||
|
||||
public static void a(Map<Enchantment, Integer> map, ItemStack itemstack) {
|
||||
NBTTagList nbttaglist = new NBTTagList();
|
||||
Iterator iterator = map.entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry entry = (Entry) iterator.next();
|
||||
Enchantment enchantment = (Enchantment) entry.getKey();
|
||||
|
||||
if (enchantment != null) {
|
||||
int i = ((Integer) entry.getValue()).intValue();
|
||||
NBTTagCompound nbttagcompound = new NBTTagCompound();
|
||||
|
||||
nbttagcompound.setShort("id", (short) Enchantment.getId(enchantment));
|
||||
nbttagcompound.setShort("lvl", (short) i);
|
||||
nbttaglist.add(nbttagcompound);
|
||||
if (itemstack.getItem() == Items.ENCHANTED_BOOK) {
|
||||
ItemEnchantedBook.a(itemstack, new WeightedRandomEnchant(enchantment, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nbttaglist.isEmpty()) {
|
||||
if (itemstack.hasTag()) {
|
||||
itemstack.getTag().remove("ench");
|
||||
}
|
||||
} else if (itemstack.getItem() != Items.ENCHANTED_BOOK) {
|
||||
itemstack.a("ench", nbttaglist);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void a(EnchantmentManager.EnchantmentModifier enchantmentmanager_enchantmentmodifier, ItemStack itemstack) {
|
||||
if (!itemstack.isEmpty()) {
|
||||
NBTTagList nbttaglist = itemstack.getEnchantments();
|
||||
|
||||
for (int i = 0; i < nbttaglist.size(); ++i) {
|
||||
short short0 = nbttaglist.get(i).getShort("id");
|
||||
short short1 = nbttaglist.get(i).getShort("lvl");
|
||||
|
||||
if (Enchantment.c(short0) != null) {
|
||||
enchantmentmanager_enchantmentmodifier.a(Enchantment.c(short0), short1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void a(EnchantmentManager.EnchantmentModifier enchantmentmanager_enchantmentmodifier, Iterable<ItemStack> iterable) {
|
||||
Iterator iterator = iterable.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
ItemStack itemstack = (ItemStack) iterator.next();
|
||||
|
||||
a(enchantmentmanager_enchantmentmodifier, itemstack);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static int a(Iterable<ItemStack> iterable, DamageSource damagesource) {
|
||||
EnchantmentManager.a.a = 0;
|
||||
EnchantmentManager.a.b = damagesource;
|
||||
a(EnchantmentManager.a, iterable);
|
||||
return EnchantmentManager.a.a;
|
||||
}
|
||||
|
||||
public static float a(ItemStack itemstack, EnumMonsterType enummonstertype) {
|
||||
EnchantmentManager.b.a = 0.0F;
|
||||
EnchantmentManager.b.b = enummonstertype;
|
||||
a(EnchantmentManager.b, itemstack);
|
||||
return EnchantmentManager.b.a;
|
||||
}
|
||||
|
||||
public static float a(EntityLiving entityliving) {
|
||||
int i = a(Enchantments.r, entityliving);
|
||||
|
||||
return i > 0 ? EnchantmentSweeping.e(i) : 0.0F;
|
||||
}
|
||||
|
||||
public static void a(EntityLiving entityliving, Entity entity) {
|
||||
EnchantmentManager.c.b = entity;
|
||||
EnchantmentManager.c.a = entityliving;
|
||||
if (entityliving != null) {
|
||||
a(EnchantmentManager.c, entityliving.aQ());
|
||||
}
|
||||
|
||||
if (entity instanceof EntityHuman) {
|
||||
a(EnchantmentManager.c, entityliving.getItemInMainHand());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void b(EntityLiving entityliving, Entity entity) {
|
||||
EnchantmentManager.d.a = entityliving;
|
||||
EnchantmentManager.d.b = entity;
|
||||
if (entityliving != null) {
|
||||
a(EnchantmentManager.d, entityliving.aQ());
|
||||
}
|
||||
|
||||
if (entityliving instanceof EntityHuman) {
|
||||
a(EnchantmentManager.d, entityliving.getItemInMainHand());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static int a(Enchantment enchantment, EntityLiving entityliving) {
|
||||
List list = enchantment.a(entityliving);
|
||||
|
||||
if (list == null) {
|
||||
return 0;
|
||||
} else {
|
||||
int i = 0;
|
||||
Iterator iterator = list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
ItemStack itemstack = (ItemStack) iterator.next();
|
||||
int j = getEnchantmentLevel(enchantment, itemstack);
|
||||
|
||||
if (j > i) {
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
public static int b(EntityLiving entityliving) {
|
||||
return a(Enchantments.KNOCKBACK, entityliving);
|
||||
}
|
||||
|
||||
public static int getFireAspectEnchantmentLevel(EntityLiving entityliving) {
|
||||
return a(Enchantments.FIRE_ASPECT, entityliving);
|
||||
}
|
||||
|
||||
public static int getOxygenEnchantmentLevel(EntityLiving entityliving) {
|
||||
return a(Enchantments.OXYGEN, entityliving);
|
||||
}
|
||||
|
||||
public static int e(EntityLiving entityliving) {
|
||||
return a(Enchantments.DEPTH_STRIDER, entityliving);
|
||||
}
|
||||
|
||||
public static int getDigSpeedEnchantmentLevel(EntityLiving entityliving) {
|
||||
return a(Enchantments.DIG_SPEED, entityliving);
|
||||
}
|
||||
|
||||
public static int b(ItemStack itemstack) {
|
||||
return getEnchantmentLevel(Enchantments.LUCK, itemstack);
|
||||
}
|
||||
|
||||
public static int c(ItemStack itemstack) {
|
||||
return getEnchantmentLevel(Enchantments.LURE, itemstack);
|
||||
}
|
||||
|
||||
public static int g(EntityLiving entityliving) {
|
||||
return a(Enchantments.LOOT_BONUS_MOBS, entityliving);
|
||||
}
|
||||
|
||||
public static boolean h(EntityLiving entityliving) {
|
||||
return a(Enchantments.WATER_WORKER, entityliving) > 0;
|
||||
}
|
||||
|
||||
public static boolean i(EntityLiving entityliving) {
|
||||
return a(Enchantments.j, entityliving) > 0;
|
||||
}
|
||||
|
||||
public static boolean d(ItemStack itemstack) {
|
||||
return getEnchantmentLevel(Enchantments.k, itemstack) > 0;
|
||||
}
|
||||
|
||||
public static boolean shouldNotDrop(ItemStack itemstack) {
|
||||
return getEnchantmentLevel(Enchantments.D, itemstack) > 0;
|
||||
}
|
||||
|
||||
public static ItemStack getRandomEquippedItemWithEnchant(Enchantment enchantment, EntityLiving entityliving) { return b(enchantment, entityliving); } // Paper - OBFHELPER
|
||||
public static ItemStack b(Enchantment enchantment, EntityLiving entityliving) {
|
||||
List list = enchantment.a(entityliving);
|
||||
|
||||
if (list.isEmpty()) {
|
||||
return ItemStack.a;
|
||||
} else {
|
||||
ArrayList arraylist = Lists.newArrayList();
|
||||
Iterator iterator = list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
ItemStack itemstack = (ItemStack) iterator.next();
|
||||
|
||||
if (!itemstack.isEmpty() && getEnchantmentLevel(enchantment, itemstack) > 0) {
|
||||
arraylist.add(itemstack);
|
||||
}
|
||||
}
|
||||
|
||||
return arraylist.isEmpty() ? ItemStack.a : (ItemStack) arraylist.get(entityliving.getRandom().nextInt(arraylist.size()));
|
||||
}
|
||||
}
|
||||
|
||||
public static int a(Random random, int i, int j, ItemStack itemstack) {
|
||||
Item item = itemstack.getItem();
|
||||
int k = item.c();
|
||||
|
||||
if (k <= 0) {
|
||||
return 0;
|
||||
} else {
|
||||
if (j > 15) {
|
||||
j = 15;
|
||||
}
|
||||
|
||||
int l = random.nextInt(8) + 1 + (j >> 1) + random.nextInt(j + 1);
|
||||
|
||||
return i == 0 ? Math.max(l / 3, 1) : (i == 1 ? l * 2 / 3 + 1 : Math.max(l, j * 2));
|
||||
}
|
||||
}
|
||||
|
||||
public static ItemStack a(Random random, ItemStack itemstack, int i, boolean flag) {
|
||||
List list = b(random, itemstack, i, flag);
|
||||
boolean flag1 = itemstack.getItem() == Items.BOOK;
|
||||
|
||||
if (flag1) {
|
||||
itemstack = new ItemStack(Items.ENCHANTED_BOOK);
|
||||
}
|
||||
|
||||
Iterator iterator = list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
WeightedRandomEnchant weightedrandomenchant = (WeightedRandomEnchant) iterator.next();
|
||||
|
||||
if (flag1) {
|
||||
ItemEnchantedBook.a(itemstack, weightedrandomenchant);
|
||||
} else {
|
||||
itemstack.addEnchantment(weightedrandomenchant.enchantment, weightedrandomenchant.level);
|
||||
}
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
public static List<WeightedRandomEnchant> b(Random random, ItemStack itemstack, int i, boolean flag) {
|
||||
ArrayList arraylist = Lists.newArrayList();
|
||||
Item item = itemstack.getItem();
|
||||
int j = item.c();
|
||||
|
||||
if (j <= 0) {
|
||||
return arraylist;
|
||||
} else {
|
||||
i += 1 + random.nextInt(j / 4 + 1) + random.nextInt(j / 4 + 1);
|
||||
float f = (random.nextFloat() + random.nextFloat() - 1.0F) * 0.15F;
|
||||
|
||||
i = MathHelper.clamp(Math.round(i + i * f), 1, Integer.MAX_VALUE);
|
||||
List list = a(i, itemstack, flag);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
arraylist.add(WeightedRandom.a(random, list));
|
||||
|
||||
while (random.nextInt(50) <= i) {
|
||||
a(list, (WeightedRandomEnchant) SystemUtils.a(arraylist));
|
||||
if (list.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
arraylist.add(WeightedRandom.a(random, list));
|
||||
i /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return arraylist;
|
||||
}
|
||||
}
|
||||
|
||||
public static void a(List<WeightedRandomEnchant> list, WeightedRandomEnchant weightedrandomenchant) {
|
||||
Iterator iterator = list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
if (!weightedrandomenchant.enchantment.c(((WeightedRandomEnchant) iterator.next()).enchantment)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static List<WeightedRandomEnchant> a(int i, ItemStack itemstack, boolean flag) {
|
||||
ArrayList arraylist = Lists.newArrayList();
|
||||
Item item = itemstack.getItem();
|
||||
boolean flag1 = itemstack.getItem() == Items.BOOK;
|
||||
Iterator iterator = Enchantment.enchantments.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Enchantment enchantment = (Enchantment) iterator.next();
|
||||
|
||||
if ((!enchantment.isTreasure() || flag) && (enchantment.itemTarget.canEnchant(item) || flag1)) {
|
||||
for (int j = enchantment.getMaxLevel(); j > enchantment.getStartLevel() - 1; --j) {
|
||||
if (i >= enchantment.a(j) && i <= enchantment.b(j)) {
|
||||
arraylist.add(new WeightedRandomEnchant(enchantment, j));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arraylist;
|
||||
}
|
||||
|
||||
public static final class EnchantmentModifierArthropods implements EnchantmentManager.EnchantmentModifier { // Akarin - private -> public
|
||||
|
||||
public EntityLiving a;
|
||||
public Entity b;
|
||||
|
||||
private EnchantmentModifierArthropods() {}
|
||||
|
||||
@Override
|
||||
public void a(Enchantment enchantment, int i) {
|
||||
enchantment.a(this.a, this.b, i);
|
||||
}
|
||||
|
||||
EnchantmentModifierArthropods(Object object) {
|
||||
this();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class EnchantmentModifierThorns implements EnchantmentManager.EnchantmentModifier { // Akarin - private -> public
|
||||
|
||||
public EntityLiving a;
|
||||
public Entity b;
|
||||
|
||||
private EnchantmentModifierThorns() {}
|
||||
|
||||
@Override
|
||||
public void a(Enchantment enchantment, int i) {
|
||||
enchantment.b(this.a, this.b, i);
|
||||
}
|
||||
|
||||
EnchantmentModifierThorns(Object object) {
|
||||
this();
|
||||
}
|
||||
}
|
||||
|
||||
static final class EnchantmentModifierDamage implements EnchantmentManager.EnchantmentModifier {
|
||||
|
||||
public float a;
|
||||
public EnumMonsterType b;
|
||||
|
||||
private EnchantmentModifierDamage() {}
|
||||
|
||||
@Override
|
||||
public void a(Enchantment enchantment, int i) {
|
||||
this.a += enchantment.a(i, this.b);
|
||||
}
|
||||
|
||||
EnchantmentModifierDamage(Object object) {
|
||||
this();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class EnchantmentModifierProtection implements EnchantmentManager.EnchantmentModifier { // Akarin - private -> public
|
||||
|
||||
public int a;
|
||||
public DamageSource b;
|
||||
|
||||
private EnchantmentModifierProtection() {}
|
||||
|
||||
@Override
|
||||
public void a(Enchantment enchantment, int i) {
|
||||
this.a += enchantment.a(i, this.b);
|
||||
}
|
||||
|
||||
EnchantmentModifierProtection(Object object) {
|
||||
this();
|
||||
}
|
||||
}
|
||||
|
||||
public interface EnchantmentModifier { // Akarin - private -> public
|
||||
|
||||
void a(Enchantment enchantment, int i);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,25 +2,26 @@ package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import io.akarin.api.internal.mixin.IMixinWorldServer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Add lock for entries set operations (safety issue)
|
||||
* 1) Made collections and entry accesses thread-safe (safety issue)
|
||||
*/
|
||||
public class EntityTracker {
|
||||
|
||||
private static final Logger a = LogManager.getLogger();
|
||||
private final WorldServer world;
|
||||
private final Set<EntityTrackerEntry> c = Sets.newHashSet();
|
||||
public final ReentrantLock entriesLock = new ReentrantLock(); // Akarin - add lock
|
||||
public final IntHashMap<EntityTrackerEntry> trackedEntities = new IntHashMap();
|
||||
private int e;
|
||||
|
||||
@@ -38,7 +39,7 @@ public class EntityTracker {
|
||||
this.addEntity(entity, 512, 2);
|
||||
EntityPlayer entityplayer = (EntityPlayer) entity;
|
||||
Iterator iterator = this.c.iterator();
|
||||
entriesLock.lock(); // Akarin
|
||||
// entriesLock.writeLock().lock(); // Akarin - locked in EntityPlayer
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||
@@ -47,7 +48,7 @@ public class EntityTracker {
|
||||
entitytrackerentry.updatePlayer(entityplayer);
|
||||
}
|
||||
}
|
||||
entriesLock.unlock(); // Akarin
|
||||
// entriesLock.writeLock().unlock(); // Akarin - locked in EntityPlayer
|
||||
} else if (entity instanceof EntityFishingHook) {
|
||||
this.addEntity(entity, 64, 5, true);
|
||||
} else if (entity instanceof EntityArrow) {
|
||||
@@ -114,46 +115,37 @@ 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())) {
|
||||
throw new IllegalStateException("Entity is already tracked!");
|
||||
}
|
||||
|
||||
EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entity, i, this.e, j, flag);
|
||||
|
||||
entriesLock.lock(); // Akarin
|
||||
this.c.add(entitytrackerentry);
|
||||
|
||||
this.trackedEntities.a(entity.getId(), entitytrackerentry);
|
||||
entitytrackerentry.scanPlayers(this.world.players);
|
||||
entriesLock.unlock(); // Akarin
|
||||
// entriesLock.writeLock().unlock(); // Akarin - locked from track method
|
||||
} catch (Throwable throwable) {
|
||||
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);
|
||||
@@ -166,34 +158,33 @@ public class EntityTracker {
|
||||
|
||||
public void untrackEntity(Entity entity) {
|
||||
org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot
|
||||
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
|
||||
if (entity instanceof EntityPlayer) {
|
||||
EntityPlayer entityplayer = (EntityPlayer) entity;
|
||||
Iterator iterator = this.c.iterator();
|
||||
entriesLock.lock(); // Akarin
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||
|
||||
entitytrackerentry.a(entityplayer);
|
||||
}
|
||||
entriesLock.unlock(); // Akarin
|
||||
}
|
||||
|
||||
EntityTrackerEntry entitytrackerentry1 = this.trackedEntities.d(entity.getId());
|
||||
EntityTrackerEntry entitytrackerentry1 = (EntityTrackerEntry) this.trackedEntities.d(entity.getId());
|
||||
|
||||
if (entitytrackerentry1 != null) {
|
||||
entriesLock.lock(); // Akarin
|
||||
this.c.remove(entitytrackerentry1);
|
||||
entitytrackerentry1.a();
|
||||
entriesLock.unlock(); // Akarin
|
||||
}
|
||||
|
||||
} // Akarin
|
||||
}
|
||||
|
||||
public void updatePlayers() {
|
||||
ArrayList arraylist = Lists.newArrayList();
|
||||
Iterator iterator = this.c.iterator();
|
||||
world.timings.tracker1.startTiming(); // Spigot
|
||||
entriesLock.lock(); // Akarin
|
||||
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
|
||||
while (iterator.hasNext()) {
|
||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||
|
||||
@@ -202,7 +193,7 @@ public class EntityTracker {
|
||||
Entity entity = entitytrackerentry.b();
|
||||
|
||||
if (entity instanceof EntityPlayer) {
|
||||
arraylist.add(entity);
|
||||
arraylist.add((EntityPlayer) entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -221,14 +212,15 @@ public class EntityTracker {
|
||||
}
|
||||
}
|
||||
}
|
||||
entriesLock.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.lock(); // Akarin
|
||||
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||
@@ -239,11 +231,15 @@ public class EntityTracker {
|
||||
entitytrackerentry.updatePlayer(entityplayer);
|
||||
}
|
||||
}
|
||||
entriesLock.unlock(); // Akarin
|
||||
|
||||
} // Akarin
|
||||
}
|
||||
|
||||
public void a(Entity entity, Packet<?> packet) {
|
||||
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
|
||||
EntityTrackerEntry entitytrackerentry; // Akarin
|
||||
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
|
||||
entitytrackerentry = (EntityTrackerEntry) this.trackedEntities.get(entity.getId());
|
||||
} // Akarin
|
||||
|
||||
if (entitytrackerentry != null) {
|
||||
entitytrackerentry.broadcast(packet);
|
||||
@@ -252,7 +248,10 @@ public class EntityTracker {
|
||||
}
|
||||
|
||||
public void sendPacketToEntity(Entity entity, Packet<?> packet) {
|
||||
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
|
||||
EntityTrackerEntry entitytrackerentry; // Akarin
|
||||
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
|
||||
entitytrackerentry = (EntityTrackerEntry) this.trackedEntities.get(entity.getId());
|
||||
} // Akarin
|
||||
|
||||
if (entitytrackerentry != null) {
|
||||
entitytrackerentry.broadcastIncludingSelf(packet);
|
||||
@@ -262,38 +261,39 @@ public class EntityTracker {
|
||||
|
||||
public void untrackPlayer(EntityPlayer entityplayer) {
|
||||
Iterator iterator = this.c.iterator();
|
||||
entriesLock.lock(); // Akarin
|
||||
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||
|
||||
entitytrackerentry.clear(entityplayer);
|
||||
}
|
||||
entriesLock.unlock(); // Akarin
|
||||
|
||||
} // Akarin
|
||||
}
|
||||
|
||||
public void a(EntityPlayer entityplayer, Chunk chunk) {
|
||||
ArrayList arraylist = Lists.newArrayList();
|
||||
ArrayList arraylist1 = Lists.newArrayList();
|
||||
Iterator iterator = this.c.iterator();
|
||||
entriesLock.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.unlock(); // Akarin
|
||||
} // Akarin
|
||||
|
||||
Entity entity1;
|
||||
|
||||
@@ -320,13 +320,14 @@ public class EntityTracker {
|
||||
public void a(int i) {
|
||||
this.e = (i - 1) * 16;
|
||||
Iterator iterator = this.c.iterator();
|
||||
entriesLock.lock(); // Akarin
|
||||
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||
|
||||
entitytrackerentry.a(this.e);
|
||||
}
|
||||
entriesLock.unlock(); // Akarin
|
||||
|
||||
} // Akarin
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,658 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
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
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Make trackedPlayerMap thread-safe (safety issue)
|
||||
*/
|
||||
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.concurrent.ConcurrentHashMap<EntityPlayer, Boolean>(); // Akarin - make concurrent
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
if (this.tracker instanceof EntityLiving) {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
82
sources/src/main/java/net/minecraft/server/FileIOThread.java
Normal file
82
sources/src/main/java/net/minecraft/server/FileIOThread.java
Normal file
@@ -0,0 +1,82 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Multi-threaded chunk saving (performance)
|
||||
*/
|
||||
public class FileIOThread implements Runnable {
|
||||
|
||||
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 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.b;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (true) {
|
||||
this.c();
|
||||
}
|
||||
}
|
||||
|
||||
private void c() {
|
||||
for (int i = 0; i < this.c.size(); ++i) {
|
||||
IAsyncChunkSaver iasyncchunksaver = (IAsyncChunkSaver) this.c.get(i);
|
||||
boolean flag = iasyncchunksaver.a();
|
||||
|
||||
if (!flag) {
|
||||
this.c.remove(i--);
|
||||
++this.e;
|
||||
}
|
||||
|
||||
if (com.destroystokyo.paper.PaperConfig.enableFileIOThreadSleep) { // Paper
|
||||
try {
|
||||
Thread.sleep(this.f ? 0L : 1L); // Paper
|
||||
} catch (InterruptedException interruptedexception) {
|
||||
interruptedexception.printStackTrace();
|
||||
}} // Paper
|
||||
}
|
||||
|
||||
if (this.c.isEmpty()) {
|
||||
try {
|
||||
Thread.sleep(25L);
|
||||
} catch (InterruptedException interruptedexception1) {
|
||||
interruptedexception1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(IAsyncChunkSaver iasyncchunksaver) {
|
||||
if (!this.c.contains(iasyncchunksaver)) {
|
||||
++this.d;
|
||||
this.c.add(iasyncchunksaver);
|
||||
}
|
||||
}
|
||||
|
||||
public void b() throws InterruptedException {
|
||||
this.f = true;
|
||||
|
||||
while (this.d != this.e) {
|
||||
Thread.sleep(10L);
|
||||
}
|
||||
|
||||
this.f = false;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
134
sources/src/main/java/net/minecraft/server/ItemMonsterEgg.java
Normal file
134
sources/src/main/java/net/minecraft/server/ItemMonsterEgg.java
Normal file
@@ -0,0 +1,134 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
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 {
|
||||
|
||||
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 EnumInteractionResult a(ItemActionContext itemactioncontext) {
|
||||
World world = itemactioncontext.getWorld();
|
||||
|
||||
if (world.isClientSide) {
|
||||
return EnumInteractionResult.SUCCESS;
|
||||
} 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.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());
|
||||
|
||||
if (entitytypes != null) {
|
||||
mobspawnerabstract.setMobName(entitytypes);
|
||||
tileentity.update();
|
||||
world.notify(blockposition, iblockdata, iblockdata, 3);
|
||||
}
|
||||
|
||||
itemstack.subtract(1);
|
||||
return EnumInteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
BlockPosition blockposition1;
|
||||
|
||||
if (iblockdata.h(world, blockposition).b()) {
|
||||
blockposition1 = blockposition;
|
||||
} else {
|
||||
blockposition1 = blockposition.shift(enumdirection);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public InteractionResultWrapper<ItemStack> a(World world, EntityHuman entityhuman, EnumHand enumhand) {
|
||||
ItemStack itemstack = entityhuman.b(enumhand);
|
||||
|
||||
if (world.isClientSide) {
|
||||
return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack);
|
||||
} else {
|
||||
MovingObjectPosition movingobjectposition = this.a(world, entityhuman, true);
|
||||
|
||||
if (movingobjectposition != null && movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.BLOCK) {
|
||||
BlockPosition blockposition = movingobjectposition.a();
|
||||
|
||||
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)) {
|
||||
EntityTypes entitytypes = this.b(itemstack.getTag());
|
||||
|
||||
if (entitytypes != null && entitytypes.a(world, itemstack, entityhuman, blockposition, false, false) != null) {
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack.subtract(1);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 EntityTypes<?> b(@Nullable NBTTagCompound nbttagcompound) {
|
||||
if (nbttagcompound != null && nbttagcompound.hasKeyOfType("EntityTag", 10)) {
|
||||
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("EntityTag");
|
||||
|
||||
if (nbttagcompound1.hasKeyOfType("id", 8)) {
|
||||
return EntityTypes.a(nbttagcompound1.getString("id"));
|
||||
}
|
||||
}
|
||||
|
||||
return this.d;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,28 +3,24 @@ package net.minecraft.server;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
|
||||
|
||||
import io.akarin.api.internal.utils.CheckedConcurrentLinkedQueue;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.DefaultEventLoopGroup;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||
import io.netty.channel.local.LocalChannel;
|
||||
import io.netty.channel.local.LocalEventLoopGroup;
|
||||
import io.netty.channel.local.LocalServerChannel;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.handler.timeout.TimeoutException;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Queue;
|
||||
import 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;
|
||||
@@ -33,10 +29,7 @@ import org.apache.logging.log4j.MarkerManager;
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Add volatile to fields (nsc)
|
||||
* 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<?>> {
|
||||
|
||||
@@ -44,50 +37,35 @@ 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());
|
||||
}
|
||||
|
||||
@Override
|
||||
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());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object init() {
|
||||
return this.a();
|
||||
}
|
||||
};
|
||||
public static final LazyInitVar<LocalEventLoopGroup> f = new LazyInitVar() {
|
||||
protected LocalEventLoopGroup a() {
|
||||
return new LocalEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object init() {
|
||||
return this.a();
|
||||
}
|
||||
};
|
||||
public static final LazyInitVar<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
|
||||
public SocketAddress l;
|
||||
public java.util.UUID spoofedUUID;
|
||||
public com.mojang.authlib.properties.Property[] spoofedProfile;
|
||||
public volatile boolean preparing = true; // Akarin - add volatile
|
||||
public boolean preparing = true;
|
||||
// Spigot End
|
||||
private PacketListener m;
|
||||
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;
|
||||
@@ -98,7 +76,6 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
this.h = enumprotocoldirection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext channelhandlercontext) throws Exception {
|
||||
super.channelActive(channelhandlercontext);
|
||||
this.channel = channelhandlercontext.channel();
|
||||
@@ -121,37 +98,58 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
NetworkManager.g.debug("Enabled auto read");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext channelhandlercontext) throws Exception {
|
||||
this.close(new ChatMessage("disconnect.endOfStream", new Object[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) throws Exception {
|
||||
ChatMessage chatmessage;
|
||||
|
||||
if (throwable instanceof TimeoutException) {
|
||||
chatmessage = new ChatMessage("disconnect.timeout", new Object[0]);
|
||||
public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) {
|
||||
if (throwable instanceof SkipEncodeException) {
|
||||
NetworkManager.g.debug("Skipping packet due to errors", throwable.getCause());
|
||||
} else {
|
||||
chatmessage = new ChatMessage("disconnect.genericReason", new Object[] { "Internal Exception: " + throwable});
|
||||
}
|
||||
boolean flag = !this.v;
|
||||
|
||||
NetworkManager.g.debug(chatmessage.toPlainText(), throwable);
|
||||
this.close(chatmessage);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
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);
|
||||
@@ -159,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();
|
||||
}
|
||||
@@ -174,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);
|
||||
@@ -207,35 +194,32 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
ChannelFuture channelfuture = this.channel.writeAndFlush(packet);
|
||||
|
||||
if (agenericfuturelistener != null) {
|
||||
channelfuture.addListeners(agenericfuturelistener);
|
||||
if (genericfuturelistener != null) {
|
||||
channelfuture.addListener(genericfuturelistener);
|
||||
}
|
||||
|
||||
channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
} else {
|
||||
this.channel.eventLoop().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
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 - 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;
|
||||
@@ -245,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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,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() {
|
||||
@@ -315,6 +306,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
return this.m;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IChatBaseComponent j() {
|
||||
return this.n;
|
||||
}
|
||||
@@ -365,19 +357,19 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet object) throws Exception { // CraftBukkit - fix decompile error
|
||||
this.a(channelhandlercontext, object);
|
||||
this.a(channelhandlercontext, (Packet) object);
|
||||
}
|
||||
|
||||
public static class QueuedPacket { // Akarin - default -> public
|
||||
public static class QueuedPacket { // Akarin
|
||||
|
||||
private final Packet<?> a; public final Packet<?> getPacket() { return this.a; } // Paper - Anti-Xray - OBFHELPER // Akarin - private -> public
|
||||
private final GenericFutureListener<? extends Future<? super Void>>[] b; public final GenericFutureListener<? extends Future<? super Void>>[] getGenericFutureListeners() { return this.b; } // Paper - Anti-Xray - OBFHELPER // Akarin - private -> public
|
||||
private final Packet<?> a; public final Packet<?> getPacket() { return this.a; } // Paper - OBFHELPER // Akarin
|
||||
@Nullable
|
||||
private final GenericFutureListener<? extends Future<? super Void>> b; public final GenericFutureListener<? extends Future<? super Void>> getGenericFutureListener() { return this.b; } // Paper - OBFHELPER // Akarin
|
||||
|
||||
public QueuedPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>>... agenericfuturelistener) {
|
||||
public QueuedPacket(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> genericfuturelistener) {
|
||||
this.a = packet;
|
||||
this.b = agenericfuturelistener;
|
||||
this.b = genericfuturelistener;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
588
sources/src/main/java/net/minecraft/server/PlayerChunkMap.java
Normal file
588
sources/src/main/java/net/minecraft/server/PlayerChunkMap.java
Normal file
@@ -0,0 +1,588 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import co.aikar.timings.Timing;
|
||||
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;
|
||||
import com.google.common.collect.Sets;
|
||||
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.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Predicate;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
// CraftBukkit start
|
||||
import java.util.LinkedList;
|
||||
// CraftBukkit end
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Add synchronizations (safety issue)
|
||||
*/
|
||||
public class PlayerChunkMap {
|
||||
|
||||
private static final Predicate<EntityPlayer> a = (entityplayer) -> {
|
||||
return entityplayer != null && !entityplayer.isSpectator();
|
||||
};
|
||||
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 Long2ObjectMap<PlayerChunk> e = new Long2ObjectOpenHashMap(4096);
|
||||
private final Set<PlayerChunk> f = Sets.newHashSet();
|
||||
private final List<PlayerChunk> g = Lists.newLinkedList();
|
||||
private final List<PlayerChunk> h = Lists.newLinkedList();
|
||||
private final List<PlayerChunk> i = Lists.newCopyOnWriteArrayList(); // Akarin - bad plugin will access this
|
||||
private int j; public int getViewDistance() { return j; } // Paper OBFHELPER
|
||||
private long k;
|
||||
private boolean l = true;
|
||||
private boolean m = true;
|
||||
private boolean wasNotEmpty; // CraftBukkit - add field
|
||||
|
||||
public PlayerChunkMap(WorldServer worldserver) {
|
||||
this.world = worldserver;
|
||||
this.a(worldserver.spigotConfig.viewDistance); // Spigot
|
||||
}
|
||||
|
||||
public WorldServer getWorld() {
|
||||
return this.world;
|
||||
}
|
||||
|
||||
public Iterator<Chunk> b() {
|
||||
final Iterator iterator = this.i.iterator();
|
||||
|
||||
return new AbstractIterator() {
|
||||
protected Chunk a() {
|
||||
while (true) {
|
||||
if (iterator.hasNext()) {
|
||||
PlayerChunk playerchunk = (PlayerChunk) iterator.next();
|
||||
Chunk chunk = playerchunk.f();
|
||||
|
||||
if (chunk == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!chunk.v()) {
|
||||
return chunk;
|
||||
}
|
||||
|
||||
if (!playerchunk.a(128.0D, PlayerChunkMap.a)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
return (Chunk) this.endOfData();
|
||||
}
|
||||
}
|
||||
|
||||
protected Object computeNext() {
|
||||
return this.a();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
this.k = i;
|
||||
|
||||
for (j = 0; j < this.i.size(); ++j) {
|
||||
playerchunk = (PlayerChunk) this.i.get(j);
|
||||
playerchunk.d();
|
||||
playerchunk.c();
|
||||
}
|
||||
} // Paper timing
|
||||
}
|
||||
|
||||
if (!this.f.isEmpty()) {
|
||||
try (Timing ignored = world.timings.doChunkMapToUpdate.startTiming()) { // Paper
|
||||
Iterator iterator = this.f.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
playerchunk = (PlayerChunk) iterator.next();
|
||||
playerchunk.d();
|
||||
}
|
||||
|
||||
this.f.clear();
|
||||
} // Paper timing
|
||||
}
|
||||
|
||||
if (this.l && i % 4L == 0L) {
|
||||
this.l = false;
|
||||
try (Timing ignored = world.timings.doChunkMapSortMissing.startTiming()) { // Paper
|
||||
// CraftBukkit start
|
||||
Collections.sort(this.h, (playerchunkx, playerchunk1x) -> {
|
||||
return ComparisonChain.start().compare(playerchunkx.g(), playerchunk1x.g()).result();
|
||||
});
|
||||
} // Paper timing
|
||||
}
|
||||
|
||||
if (this.m && i % 4L == 2L) {
|
||||
this.m = false;
|
||||
try (Timing ignored = world.timings.doChunkMapSortSendToPlayers.startTiming()) { // Paper
|
||||
Collections.sort(this.g, (playerchunkx, playerchunk1x) -> {
|
||||
return ComparisonChain.start().compare(playerchunkx.g(), playerchunk1x.g()).result();
|
||||
});
|
||||
} // Paper timing
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
if (!this.h.isEmpty()) {
|
||||
try (Timing ignored = world.timings.doChunkMapPlayersNeedingChunks.startTiming()) { // Paper
|
||||
// Spigot start
|
||||
org.spigotmc.SlackActivityAccountant activityAccountant = this.world.getMinecraftServer().slackActivityAccountant;
|
||||
activityAccountant.startActivity(0.5);
|
||||
int chunkGensAllowed = world.paperConfig.maxChunkGensPerTick; // Paper
|
||||
// Spigot end
|
||||
|
||||
Iterator iterator1 = this.h.iterator();
|
||||
|
||||
while (iterator1.hasNext()) {
|
||||
PlayerChunk playerchunk1 = (PlayerChunk) iterator1.next();
|
||||
|
||||
if (playerchunk1.f() == null) {
|
||||
boolean flag = playerchunk1.a(PlayerChunkMap.b);
|
||||
// Paper start
|
||||
if (flag && !playerchunk1.chunkExists && chunkGensAllowed-- <= 0) {
|
||||
continue;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
if (playerchunk1.a(flag)) {
|
||||
iterator1.remove();
|
||||
if (playerchunk1.b()) {
|
||||
this.g.remove(playerchunk1);
|
||||
}
|
||||
|
||||
if (activityAccountant.activityTimeIsExhausted()) { // Spigot
|
||||
break;
|
||||
}
|
||||
}
|
||||
// CraftBukkit start - SPIGOT-2891: remove once chunk has been provided
|
||||
} else {
|
||||
iterator1.remove();
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
activityAccountant.endActivity(); // Spigot
|
||||
} // Paper timing
|
||||
}
|
||||
|
||||
if (!this.g.isEmpty()) {
|
||||
j = world.paperConfig.maxChunkSendsPerTick; // Paper
|
||||
try (Timing ignored = world.timings.doChunkMapPendingSendToPlayers.startTiming()) { // Paper
|
||||
Iterator iterator2 = this.g.iterator();
|
||||
|
||||
while (iterator2.hasNext()) {
|
||||
PlayerChunk playerchunk2 = (PlayerChunk) iterator2.next();
|
||||
|
||||
if (playerchunk2.b()) {
|
||||
iterator2.remove();
|
||||
--j;
|
||||
if (j < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Paper timing
|
||||
}
|
||||
|
||||
if (this.managedPlayers.isEmpty()) {
|
||||
try (Timing ignored = world.timings.doChunkMapUnloadChunks.startTiming()) { // Paper
|
||||
WorldProvider worldprovider = this.world.worldProvider;
|
||||
|
||||
if (!worldprovider.p() && !this.world.savingDisabled) { // Paper - respect saving disabled setting
|
||||
this.world.getChunkProviderServer().b();
|
||||
}
|
||||
} // Paper timing
|
||||
}
|
||||
} // Akarin
|
||||
|
||||
}
|
||||
|
||||
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 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) {
|
||||
long k = d(i, j);
|
||||
PlayerChunk playerchunk = (PlayerChunk) this.e.get(k);
|
||||
|
||||
if (playerchunk == null) {
|
||||
playerchunk = new PlayerChunk(this, i, j);
|
||||
this.e.put(k, playerchunk);
|
||||
this.i.add(playerchunk);
|
||||
if (playerchunk.f() == null) {
|
||||
this.h.add(playerchunk);
|
||||
}
|
||||
|
||||
if (!playerchunk.b()) {
|
||||
this.g.add(playerchunk);
|
||||
}
|
||||
}
|
||||
|
||||
return playerchunk;
|
||||
}
|
||||
|
||||
// CraftBukkit start - add method
|
||||
public final boolean isChunkInUse(int x, int z) {
|
||||
PlayerChunk pi = getChunk(x, z);
|
||||
if (pi != null) {
|
||||
return (pi.c.size() > 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
public void flagDirty(BlockPosition blockposition) {
|
||||
int i = blockposition.getX() >> 4;
|
||||
int j = blockposition.getZ() >> 4;
|
||||
PlayerChunk playerchunk = this.getChunk(i, j);
|
||||
|
||||
if (playerchunk != null) {
|
||||
playerchunk.a(blockposition.getX() & 15, blockposition.getY(), blockposition.getZ() & 15);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void addPlayer(EntityPlayer entityplayer) {
|
||||
int i = (int) entityplayer.locX >> 4;
|
||||
int j = (int) entityplayer.locZ >> 4;
|
||||
|
||||
entityplayer.d = entityplayer.locX;
|
||||
entityplayer.e = entityplayer.locZ;
|
||||
|
||||
|
||||
// CraftBukkit start - Load nearby chunks first
|
||||
List<ChunkCoordIntPair> chunkList = new LinkedList<ChunkCoordIntPair>();
|
||||
|
||||
// Paper start - Player view distance API
|
||||
int viewDistance = entityplayer.getViewDistance();
|
||||
for (int k = i - viewDistance; k <= i + viewDistance; ++k) {
|
||||
for (int l = j - viewDistance; l <= j + viewDistance; ++l) {
|
||||
// Paper end
|
||||
chunkList.add(new ChunkCoordIntPair(k, l));
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(chunkList, new ChunkCoordComparator(entityplayer));
|
||||
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
|
||||
for (ChunkCoordIntPair pair : chunkList) {
|
||||
this.c(pair.x, pair.z).a(entityplayer);
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
this.managedPlayers.add(entityplayer);
|
||||
} // Akarin
|
||||
this.e();
|
||||
}
|
||||
|
||||
public void removePlayer(EntityPlayer entityplayer) {
|
||||
int i = (int) entityplayer.d >> 4;
|
||||
int j = (int) entityplayer.e >> 4;
|
||||
|
||||
// Paper start - Player view distance API
|
||||
int viewDistance = entityplayer.getViewDistance();
|
||||
for (int k = i - viewDistance; k <= i + viewDistance; ++k) {
|
||||
for (int l = j - viewDistance; l <= j + viewDistance; ++l) {
|
||||
// Paper end
|
||||
PlayerChunk playerchunk = this.getChunk(k, l);
|
||||
|
||||
if (playerchunk != null) {
|
||||
playerchunk.b(entityplayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
|
||||
this.managedPlayers.remove(entityplayer);
|
||||
} // Akarin
|
||||
this.e();
|
||||
}
|
||||
|
||||
private boolean a(int i, int j, int k, int l, int i1) {
|
||||
int j1 = i - k;
|
||||
int k1 = j - l;
|
||||
|
||||
return j1 >= -i1 && j1 <= i1 ? k1 >= -i1 && k1 <= i1 : false;
|
||||
}
|
||||
|
||||
public void movePlayer(EntityPlayer entityplayer) {
|
||||
int i = (int) entityplayer.locX >> 4;
|
||||
int j = (int) entityplayer.locZ >> 4;
|
||||
double d0 = entityplayer.d - entityplayer.locX;
|
||||
double d1 = entityplayer.e - entityplayer.locZ;
|
||||
double d2 = d0 * d0 + d1 * d1;
|
||||
|
||||
if (d2 >= 64.0D) {
|
||||
int k = (int) entityplayer.d >> 4;
|
||||
int l = (int) entityplayer.e >> 4;
|
||||
int i1 = entityplayer.getViewDistance(); // Paper - Player view distance API
|
||||
|
||||
int j1 = i - k;
|
||||
int k1 = j - l;
|
||||
|
||||
List<ChunkCoordIntPair> chunksToLoad = new LinkedList<ChunkCoordIntPair>(); // CraftBukkit
|
||||
|
||||
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, i1)) {
|
||||
// this.c(l1, i2).a(entityplayer);
|
||||
chunksToLoad.add(new ChunkCoordIntPair(l1, i2)); // CraftBukkit
|
||||
}
|
||||
|
||||
if (!this.a(l1 - j1, i2 - k1, i, j, i1)) {
|
||||
PlayerChunk playerchunk = this.getChunk(l1 - j1, i2 - k1);
|
||||
|
||||
if (playerchunk != null) {
|
||||
playerchunk.b(entityplayer);
|
||||
}
|
||||
} else { // Paper start
|
||||
PlayerChunk playerchunk = this.getChunk(l1 - j1, i2 - k1);
|
||||
if (playerchunk != null) {
|
||||
playerchunk.checkHighPriority(entityplayer); // Paper
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
}
|
||||
|
||||
entityplayer.d = entityplayer.locX;
|
||||
entityplayer.e = entityplayer.locZ;
|
||||
this.e();
|
||||
|
||||
// CraftBukkit start - send nearest chunks first
|
||||
Collections.sort(chunksToLoad, new ChunkCoordComparator(entityplayer));
|
||||
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
|
||||
for (ChunkCoordIntPair pair : chunksToLoad) {
|
||||
// Paper start
|
||||
PlayerChunk c = this.c(pair.x, pair.z);
|
||||
c.checkHighPriority(entityplayer);
|
||||
c.a(entityplayer);
|
||||
// Paper end
|
||||
}
|
||||
} // Akarin
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(EntityPlayer entityplayer, int i, int j) {
|
||||
PlayerChunk playerchunk = this.getChunk(i, j);
|
||||
|
||||
return playerchunk != null && playerchunk.d(entityplayer) && playerchunk.e();
|
||||
}
|
||||
|
||||
public final void setViewDistanceForAll(int viewDistance) { this.a(viewDistance); } // Paper - OBFHELPER
|
||||
// Paper start - Separate into two methods
|
||||
public void a(int i) {
|
||||
i = MathHelper.clamp(i, 3, 32);
|
||||
if (i != this.j) {
|
||||
int j = i - this.j;
|
||||
ArrayList arraylist; // Akarin
|
||||
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
|
||||
arraylist = Lists.newArrayList(this.managedPlayers);
|
||||
} // Akarin
|
||||
Iterator iterator = arraylist.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
|
||||
this.setViewDistance(entityplayer, i, false); // Paper - Split, don't mark sort pending, we'll handle it after
|
||||
}
|
||||
|
||||
this.j = i;
|
||||
this.e();
|
||||
}
|
||||
}
|
||||
|
||||
public void setViewDistance(EntityPlayer entityplayer, int i) {
|
||||
this.setViewDistance(entityplayer, i, true); // Mark sort pending by default so we don't have to remember to do so all the time
|
||||
}
|
||||
|
||||
// Copied from above with minor changes
|
||||
public void setViewDistance(EntityPlayer entityplayer, int i, boolean markSort) {
|
||||
i = MathHelper.clamp(i, 3, 32);
|
||||
int oldViewDistance = entityplayer.getViewDistance();
|
||||
if (i != oldViewDistance) {
|
||||
int j = i - oldViewDistance;
|
||||
|
||||
int k = (int) entityplayer.locX >> 4;
|
||||
int l = (int) entityplayer.locZ >> 4;
|
||||
int i1;
|
||||
int j1;
|
||||
|
||||
synchronized (((IMixinWorldServer) world).trackLock()) { // Akarin
|
||||
if (j > 0) {
|
||||
for (i1 = k - i; i1 <= k + i; ++i1) {
|
||||
for (j1 = l - i; j1 <= l + i; ++j1) {
|
||||
PlayerChunk playerchunk = this.c(i1, j1);
|
||||
|
||||
if (!playerchunk.d(entityplayer)) {
|
||||
playerchunk.a(entityplayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i1 = k - oldViewDistance; i1 <= k + oldViewDistance; ++i1) {
|
||||
for (j1 = l - oldViewDistance; j1 <= l + oldViewDistance; ++j1) {
|
||||
if (!this.a(i1, j1, k, l, i)) {
|
||||
this.c(i1, j1).b(entityplayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (markSort) {
|
||||
this.e();
|
||||
}
|
||||
}
|
||||
} // Akarin
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
|
||||
private void e() {
|
||||
this.l = true;
|
||||
this.m = true;
|
||||
}
|
||||
|
||||
public static int getFurthestViewableBlock(int i) {
|
||||
return i * 16 - 16;
|
||||
}
|
||||
|
||||
private static long d(int i, int j) {
|
||||
return (long) i + 2147483647L | (long) j + 2147483647L << 32;
|
||||
}
|
||||
|
||||
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 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);
|
||||
|
||||
playerchunk.c();
|
||||
this.e.remove(i);
|
||||
this.i.remove(playerchunk);
|
||||
this.f.remove(playerchunk);
|
||||
this.g.remove(playerchunk);
|
||||
this.h.remove(playerchunk);
|
||||
Chunk chunk = playerchunk.f();
|
||||
|
||||
if (chunk != null) {
|
||||
// Paper start - delay chunk unloads
|
||||
if (world.paperConfig.delayChunkUnloadsBy <= 0) {
|
||||
this.getWorld().getChunkProviderServer().unload(chunk);
|
||||
} else {
|
||||
chunk.scheduledForUnload = System.currentTimeMillis();
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
} // Akarin
|
||||
|
||||
}
|
||||
|
||||
// CraftBukkit start - Sorter to load nearby chunks first
|
||||
private static class ChunkCoordComparator implements java.util.Comparator<ChunkCoordIntPair> {
|
||||
private int x;
|
||||
private int z;
|
||||
|
||||
public ChunkCoordComparator (EntityPlayer entityplayer) {
|
||||
x = (int) entityplayer.locX >> 4;
|
||||
z = (int) entityplayer.locZ >> 4;
|
||||
}
|
||||
|
||||
public int compare(ChunkCoordIntPair a, ChunkCoordIntPair b) {
|
||||
if (a.equals(b)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Subtract current position to set center point
|
||||
int ax = a.x - this.x;
|
||||
int az = a.z - this.z;
|
||||
int bx = b.x - this.x;
|
||||
int bz = b.z - this.z;
|
||||
|
||||
int result = ((ax - bx) * (ax + bx)) + ((az - bz) * (az + bz));
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (ax < 0) {
|
||||
if (bx < 0) {
|
||||
return bz - az;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (bx < 0) {
|
||||
return 1;
|
||||
} else {
|
||||
return az - bz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
// Paper start - Player view distance API
|
||||
public void updateViewDistance(EntityPlayer player, int distanceIn) {
|
||||
final int oldViewDistance = player.getViewDistance();
|
||||
|
||||
// This represents the view distance that we will set on the player
|
||||
// It can exist as a negative value
|
||||
int playerViewDistance = MathHelper.clamp(distanceIn, 3, 32);
|
||||
|
||||
// This value is the one we actually use to update the chunk map
|
||||
// We don't ever want this to be a negative
|
||||
int toSet = playerViewDistance;
|
||||
|
||||
if (distanceIn < 0) {
|
||||
playerViewDistance = -1;
|
||||
toSet = world.getPlayerChunkMap().getViewDistance();
|
||||
}
|
||||
|
||||
if (toSet != oldViewDistance) {
|
||||
// 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
File diff suppressed because it is too large
Load Diff
@@ -1,127 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.destroystokyo.paper.exception.ServerInternalException;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import com.destroystokyo.paper.PaperConfig; // Paper
|
||||
import java.util.LinkedHashMap; // Paper
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Removes unneed synchronization (performance)
|
||||
*/
|
||||
public class RegionFileCache {
|
||||
|
||||
public static final Map<File, RegionFile> a = new LinkedHashMap(PaperConfig.regionFileCacheSize, 0.75f, true); // Spigot - private -> public, Paper - HashMap -> LinkedHashMap
|
||||
|
||||
public static synchronized RegionFile a(File file, int i, int j) {
|
||||
File file1 = new File(file, "region");
|
||||
File file2 = new File(file1, "r." + (i >> 5) + "." + (j >> 5) + ".mca");
|
||||
RegionFile regionfile = (RegionFile) RegionFileCache.a.get(file2);
|
||||
|
||||
if (regionfile != null) {
|
||||
return regionfile;
|
||||
} else {
|
||||
if (!file1.exists()) {
|
||||
file1.mkdirs();
|
||||
}
|
||||
|
||||
if (RegionFileCache.a.size() >= PaperConfig.regionFileCacheSize) { // Paper
|
||||
trimCache(); // Paper
|
||||
}
|
||||
|
||||
RegionFile regionfile1 = new RegionFile(file2);
|
||||
|
||||
RegionFileCache.a.put(file2, regionfile1);
|
||||
return regionfile1;
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized RegionFile b(File file, int i, int j) {
|
||||
File file1 = new File(file, "region");
|
||||
File file2 = new File(file1, "r." + (i >> 5) + "." + (j >> 5) + ".mca");
|
||||
RegionFile regionfile = (RegionFile) RegionFileCache.a.get(file2);
|
||||
|
||||
if (regionfile != null) {
|
||||
return regionfile;
|
||||
} else if (file1.exists() && file2.exists()) {
|
||||
if (RegionFileCache.a.size() >= 256) {
|
||||
a();
|
||||
}
|
||||
|
||||
RegionFile regionfile1 = new RegionFile(file2);
|
||||
|
||||
RegionFileCache.a.put(file2, regionfile1);
|
||||
return regionfile1;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Paper Start
|
||||
private static synchronized void trimCache() {
|
||||
Iterator<Map.Entry<File, RegionFile>> itr = RegionFileCache.a.entrySet().iterator();
|
||||
int count = RegionFileCache.a.size() - PaperConfig.regionFileCacheSize;
|
||||
while (count-- >= 0 && itr.hasNext()) {
|
||||
try {
|
||||
itr.next().getValue().c();
|
||||
} catch (IOException ioexception) {
|
||||
ioexception.printStackTrace();
|
||||
ServerInternalException.reportInternalException(ioexception);
|
||||
}
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
// Paper End
|
||||
|
||||
public static synchronized void a() {
|
||||
Iterator iterator = RegionFileCache.a.values().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
RegionFile regionfile = (RegionFile) iterator.next();
|
||||
|
||||
try {
|
||||
if (regionfile != null) {
|
||||
regionfile.c();
|
||||
}
|
||||
} catch (IOException ioexception) {
|
||||
ioexception.printStackTrace();
|
||||
ServerInternalException.reportInternalException(ioexception); // Paper
|
||||
}
|
||||
}
|
||||
|
||||
RegionFileCache.a.clear();
|
||||
}
|
||||
|
||||
// CraftBukkit start - call sites hoisted for synchronization
|
||||
public static /*synchronized*/ NBTTagCompound d(File file, int i, int j) throws IOException { // Akarin - 1.13 backport - remove synchronization // OBFHELPER: read
|
||||
RegionFile regionfile = a(file, i, j);
|
||||
|
||||
DataInputStream datainputstream = regionfile.a(i & 31, j & 31);
|
||||
|
||||
if (datainputstream == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return NBTCompressedStreamTools.a(datainputstream);
|
||||
}
|
||||
|
||||
public static /*synchronized*/ void e(File file, int i, int j, NBTTagCompound nbttagcompound) throws IOException { // Akarin - 1.13 backport - remove synchronization // OBFHELPER: write
|
||||
RegionFile regionfile = a(file, i, j);
|
||||
|
||||
DataOutputStream dataoutputstream = regionfile.b(i & 31, j & 31);
|
||||
NBTCompressedStreamTools.a(nbttagcompound, (java.io.DataOutput) dataoutputstream);
|
||||
dataoutputstream.close();
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
public static /*synchronized*/ boolean chunkExists(File file, int i, int j) { // Akarin - 1.13 backport - remove synchronization
|
||||
RegionFile regionfile = b(file, i, j);
|
||||
|
||||
return regionfile != null ? regionfile.c(i & 31, j & 31) : false;
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterators;
|
||||
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 java.util.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;
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,6 @@ import javax.annotation.Nullable;
|
||||
|
||||
// Spigot start
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -30,13 +27,12 @@ import java.util.concurrent.Callable;
|
||||
*/
|
||||
public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Paper - remove tickable
|
||||
|
||||
private int a;
|
||||
public int rotation;
|
||||
private GameProfile g;
|
||||
private int h;
|
||||
private boolean i;
|
||||
private static UserCache j;
|
||||
private static MinecraftSessionService k;
|
||||
private GameProfile a;
|
||||
private int e;
|
||||
private boolean f;
|
||||
public boolean drop = true;
|
||||
private static UserCache h;
|
||||
private static MinecraftSessionService i;
|
||||
// Spigot start
|
||||
public static final ExecutorService executor = Executors.newFixedThreadPool(3,
|
||||
new ThreadFactoryBuilder()
|
||||
@@ -80,7 +76,7 @@ public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Pa
|
||||
|
||||
if ( property == null )
|
||||
{
|
||||
profile = MinecraftServer.getServer().az().fillProfileProperties( profile, true );
|
||||
profile = TileEntitySkull.i.fillProfileProperties( profile, true );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,24 +86,24 @@ public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Pa
|
||||
} );
|
||||
// Spigot end
|
||||
|
||||
public TileEntitySkull() {}
|
||||
public TileEntitySkull() {
|
||||
super(TileEntityTypes.SKULL);
|
||||
}
|
||||
|
||||
public static void a(UserCache usercache) {
|
||||
TileEntitySkull.j = usercache;
|
||||
TileEntitySkull.h = usercache;
|
||||
}
|
||||
|
||||
public static void a(MinecraftSessionService minecraftsessionservice) {
|
||||
TileEntitySkull.k = minecraftsessionservice;
|
||||
TileEntitySkull.i = minecraftsessionservice;
|
||||
}
|
||||
|
||||
public NBTTagCompound save(NBTTagCompound nbttagcompound) {
|
||||
super.save(nbttagcompound);
|
||||
nbttagcompound.setByte("SkullType", (byte) (this.a & 255));
|
||||
nbttagcompound.setByte("Rot", (byte) (this.rotation & 255));
|
||||
if (this.g != null) {
|
||||
if (this.a != null) {
|
||||
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
|
||||
|
||||
GameProfileSerializer.serialize(nbttagcompound1, this.g);
|
||||
GameProfileSerializer.serialize(nbttagcompound1, this.a);
|
||||
nbttagcompound.set("Owner", nbttagcompound1);
|
||||
}
|
||||
|
||||
@@ -116,30 +112,27 @@ public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Pa
|
||||
|
||||
public void load(NBTTagCompound nbttagcompound) {
|
||||
super.load(nbttagcompound);
|
||||
this.a = nbttagcompound.getByte("SkullType");
|
||||
this.rotation = nbttagcompound.getByte("Rot");
|
||||
if (this.a == 3) {
|
||||
if (nbttagcompound.hasKeyOfType("Owner", 10)) {
|
||||
this.g = GameProfileSerializer.deserialize(nbttagcompound.getCompound("Owner"));
|
||||
} else if (nbttagcompound.hasKeyOfType("ExtraType", 8)) {
|
||||
String s = nbttagcompound.getString("ExtraType");
|
||||
if (nbttagcompound.hasKeyOfType("Owner", 10)) {
|
||||
this.setGameProfile(GameProfileSerializer.deserialize(nbttagcompound.getCompound("Owner")));
|
||||
} else if (nbttagcompound.hasKeyOfType("ExtraType", 8)) {
|
||||
String s = nbttagcompound.getString("ExtraType");
|
||||
|
||||
if (!UtilColor.b(s)) {
|
||||
this.g = new GameProfile((UUID) null, s);
|
||||
this.i();
|
||||
}
|
||||
if (!UtilColor.b(s)) {
|
||||
this.setGameProfile(new GameProfile((UUID) null, s));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void e() {
|
||||
if (this.a == 5) {
|
||||
public void Y_() {
|
||||
Block block = this.getBlock().getBlock();
|
||||
|
||||
if (block == Blocks.DRAGON_HEAD || block == Blocks.DRAGON_WALL_HEAD) {
|
||||
if (this.world.isBlockIndirectlyPowered(this.position)) {
|
||||
this.i = true;
|
||||
++this.h;
|
||||
this.f = true;
|
||||
++this.e;
|
||||
} else {
|
||||
this.i = false;
|
||||
this.f = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,46 +140,63 @@ public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Pa
|
||||
|
||||
@Nullable
|
||||
public GameProfile getGameProfile() {
|
||||
return this.g;
|
||||
return this.a;
|
||||
}
|
||||
|
||||
// Paper start
|
||||
static NBTTagCompound sanitizeTileEntityUUID(NBTTagCompound cmp) {
|
||||
NBTTagCompound owner = cmp.getCompound("Owner");
|
||||
if (!owner.isEmpty()) {
|
||||
sanitizeUUID(owner);
|
||||
}
|
||||
return cmp;
|
||||
}
|
||||
|
||||
static void sanitizeUUID(NBTTagCompound owner) {
|
||||
NBTTagCompound properties = owner.getCompound("Properties");
|
||||
NBTTagList list = null;
|
||||
if (!properties.isEmpty()) {
|
||||
list = properties.getList("textures", 10);
|
||||
}
|
||||
|
||||
if (list != null && !list.isEmpty()) {
|
||||
String textures = ((NBTTagCompound)list.get(0)).getString("Value");
|
||||
if (textures != null && textures.length() > 3) {
|
||||
String uuid = UUID.nameUUIDFromBytes(textures.getBytes()).toString();
|
||||
owner.setString("Id", uuid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
owner.setString("Id", UUID.randomUUID().toString());
|
||||
}
|
||||
// Paper end
|
||||
|
||||
@Nullable
|
||||
public PacketPlayOutTileEntityData getUpdatePacket() {
|
||||
return new PacketPlayOutTileEntityData(this.position, 4, this.d());
|
||||
return new PacketPlayOutTileEntityData(this.position, 4, sanitizeTileEntityUUID(this.aa_())); // Paper
|
||||
}
|
||||
|
||||
public NBTTagCompound d() {
|
||||
public NBTTagCompound aa_() {
|
||||
return this.save(new NBTTagCompound());
|
||||
}
|
||||
|
||||
public void setSkullType(int i) {
|
||||
this.a = i;
|
||||
this.g = null;
|
||||
}
|
||||
|
||||
public void setGameProfile(@Nullable GameProfile gameprofile) {
|
||||
this.a = 3;
|
||||
this.g = gameprofile;
|
||||
this.i();
|
||||
this.a = gameprofile;
|
||||
this.f();
|
||||
}
|
||||
|
||||
private void i() {
|
||||
private void f() {
|
||||
// Spigot start
|
||||
GameProfile profile = this.g;
|
||||
setSkullType( 0 ); // Work around client bug
|
||||
GameProfile profile = this.getGameProfile();
|
||||
b(profile, new Predicate<GameProfile>() {
|
||||
|
||||
@Override
|
||||
public boolean apply(GameProfile input) {
|
||||
setSkullType(3); // Work around client bug
|
||||
g = input;
|
||||
a = input;
|
||||
update();
|
||||
if (world != null) {
|
||||
world.m(position); // PAIL: notify
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}, false);
|
||||
}, false);
|
||||
// Spigot end
|
||||
}
|
||||
|
||||
@@ -241,25 +251,24 @@ public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Pa
|
||||
}
|
||||
// Spigot end
|
||||
|
||||
public int getSkullType() {
|
||||
return this.a;
|
||||
// CraftBukkit start
|
||||
public static void a(IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
setShouldDrop(iblockaccess, blockposition, false);
|
||||
}
|
||||
|
||||
public void setRotation(int i) {
|
||||
this.rotation = i;
|
||||
}
|
||||
public static void setShouldDrop(IBlockAccess iblockaccess, BlockPosition blockposition, boolean flag) {
|
||||
// CraftBukkit end
|
||||
TileEntity tileentity = iblockaccess.getTileEntity(blockposition);
|
||||
|
||||
public void a(EnumBlockMirror enumblockmirror) {
|
||||
if (this.world != null && this.world.getType(this.getPosition()).get(BlockSkull.FACING) == EnumDirection.UP) {
|
||||
this.rotation = enumblockmirror.a(this.rotation, 16);
|
||||
if (tileentity instanceof TileEntitySkull) {
|
||||
TileEntitySkull tileentityskull = (TileEntitySkull) tileentity;
|
||||
|
||||
tileentityskull.drop = flag; // CraftBukkit
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(EnumBlockRotation enumblockrotation) {
|
||||
if (this.world != null && this.world.getType(this.getPosition()).get(BlockSkull.FACING) == EnumDirection.UP) {
|
||||
this.rotation = enumblockrotation.a(this.rotation, 16);
|
||||
}
|
||||
|
||||
public boolean shouldDrop() {
|
||||
return this.drop;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,773 +0,0 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import javax.annotation.Nullable;
|
||||
// CraftBukkit start
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.weather.ThunderChangeEvent;
|
||||
import org.bukkit.event.weather.WeatherChangeEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Add volatile to fields (slack service)
|
||||
*/
|
||||
public class WorldData {
|
||||
|
||||
private String b;
|
||||
private int c;
|
||||
private boolean d;
|
||||
public static final EnumDifficulty a = EnumDifficulty.NORMAL;
|
||||
private long e;
|
||||
private WorldType f;
|
||||
private String g;
|
||||
private int h;
|
||||
private int i;
|
||||
private int j;
|
||||
private volatile long k; // Akarin - volatile - OBFHELPER: time
|
||||
private volatile long l; // Akarin - volatile - OBFHELPER: dayTime
|
||||
private long m;
|
||||
private long n;
|
||||
private NBTTagCompound o;
|
||||
private int p;
|
||||
private String levelName;
|
||||
private int r;
|
||||
private int s;
|
||||
private boolean t;
|
||||
private int u;
|
||||
private boolean v;
|
||||
private int w;
|
||||
private EnumGamemode x;
|
||||
private boolean y;
|
||||
private boolean z;
|
||||
private boolean A;
|
||||
private boolean B;
|
||||
private volatile EnumDifficulty C; // Akarin - volatile
|
||||
private boolean D;
|
||||
private double E;
|
||||
private double F;
|
||||
private double G;
|
||||
private long H;
|
||||
private double I;
|
||||
private double J;
|
||||
private double K;
|
||||
private int L;
|
||||
private int M;
|
||||
private final Map<DimensionManager, NBTTagCompound> N;
|
||||
private GameRules O;
|
||||
public WorldServer world; // CraftBukkit
|
||||
|
||||
protected WorldData() {
|
||||
this.f = WorldType.NORMAL;
|
||||
this.g = "";
|
||||
this.G = 6.0E7D;
|
||||
this.J = 5.0D;
|
||||
this.K = 0.2D;
|
||||
this.L = 5;
|
||||
this.M = 15;
|
||||
this.N = Maps.newEnumMap(DimensionManager.class);
|
||||
this.O = new GameRules();
|
||||
}
|
||||
|
||||
public static void a(DataConverterManager dataconvertermanager) {
|
||||
dataconvertermanager.a(DataConverterTypes.LEVEL, new DataInspector() {
|
||||
@Override
|
||||
public NBTTagCompound a(DataConverter dataconverter, NBTTagCompound nbttagcompound, int i) {
|
||||
if (nbttagcompound.hasKeyOfType("Player", 10)) {
|
||||
nbttagcompound.set("Player", dataconverter.a(DataConverterTypes.PLAYER, nbttagcompound.getCompound("Player"), i));
|
||||
}
|
||||
|
||||
return nbttagcompound;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public WorldData(NBTTagCompound nbttagcompound) {
|
||||
this.f = WorldType.NORMAL;
|
||||
this.g = "";
|
||||
this.G = 6.0E7D;
|
||||
this.J = 5.0D;
|
||||
this.K = 0.2D;
|
||||
this.L = 5;
|
||||
this.M = 15;
|
||||
this.N = Maps.newEnumMap(DimensionManager.class);
|
||||
this.O = new GameRules();
|
||||
NBTTagCompound nbttagcompound1;
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("Version", 10)) {
|
||||
nbttagcompound1 = nbttagcompound.getCompound("Version");
|
||||
this.b = nbttagcompound1.getString("Name");
|
||||
this.c = nbttagcompound1.getInt("Id");
|
||||
this.d = nbttagcompound1.getBoolean("Snapshot");
|
||||
}
|
||||
|
||||
this.e = nbttagcompound.getLong("RandomSeed");
|
||||
if (nbttagcompound.hasKeyOfType("generatorName", 8)) {
|
||||
String s = nbttagcompound.getString("generatorName");
|
||||
|
||||
this.f = WorldType.getType(s);
|
||||
if (this.f == null) {
|
||||
this.f = WorldType.NORMAL;
|
||||
} else if (this.f.f()) {
|
||||
int i = 0;
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("generatorVersion", 99)) {
|
||||
i = nbttagcompound.getInt("generatorVersion");
|
||||
}
|
||||
|
||||
this.f = this.f.a(i);
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("generatorOptions", 8)) {
|
||||
this.g = nbttagcompound.getString("generatorOptions");
|
||||
}
|
||||
}
|
||||
|
||||
this.x = EnumGamemode.getById(nbttagcompound.getInt("GameType"));
|
||||
if (nbttagcompound.hasKeyOfType("MapFeatures", 99)) {
|
||||
this.y = nbttagcompound.getBoolean("MapFeatures");
|
||||
} else {
|
||||
this.y = true;
|
||||
}
|
||||
|
||||
this.h = nbttagcompound.getInt("SpawnX");
|
||||
this.i = nbttagcompound.getInt("SpawnY");
|
||||
this.j = nbttagcompound.getInt("SpawnZ");
|
||||
this.k = nbttagcompound.getLong("Time");
|
||||
if (nbttagcompound.hasKeyOfType("DayTime", 99)) {
|
||||
this.l = nbttagcompound.getLong("DayTime");
|
||||
} else {
|
||||
this.l = this.k;
|
||||
}
|
||||
|
||||
this.m = nbttagcompound.getLong("LastPlayed");
|
||||
this.n = nbttagcompound.getLong("SizeOnDisk");
|
||||
this.levelName = nbttagcompound.getString("LevelName");
|
||||
this.r = nbttagcompound.getInt("version");
|
||||
this.s = nbttagcompound.getInt("clearWeatherTime");
|
||||
this.u = nbttagcompound.getInt("rainTime");
|
||||
this.t = nbttagcompound.getBoolean("raining");
|
||||
this.w = nbttagcompound.getInt("thunderTime");
|
||||
this.v = nbttagcompound.getBoolean("thundering");
|
||||
this.z = nbttagcompound.getBoolean("hardcore");
|
||||
if (nbttagcompound.hasKeyOfType("initialized", 99)) {
|
||||
this.B = nbttagcompound.getBoolean("initialized");
|
||||
} else {
|
||||
this.B = true;
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("allowCommands", 99)) {
|
||||
this.A = nbttagcompound.getBoolean("allowCommands");
|
||||
} else {
|
||||
this.A = this.x == EnumGamemode.CREATIVE;
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("Player", 10)) {
|
||||
this.o = nbttagcompound.getCompound("Player");
|
||||
this.p = this.o.getInt("Dimension");
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("GameRules", 10)) {
|
||||
this.O.a(nbttagcompound.getCompound("GameRules"));
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("Difficulty", 99)) {
|
||||
this.C = EnumDifficulty.getById(nbttagcompound.getByte("Difficulty"));
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("DifficultyLocked", 1)) {
|
||||
this.D = nbttagcompound.getBoolean("DifficultyLocked");
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("BorderCenterX", 99)) {
|
||||
this.E = nbttagcompound.getDouble("BorderCenterX");
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("BorderCenterZ", 99)) {
|
||||
this.F = nbttagcompound.getDouble("BorderCenterZ");
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("BorderSize", 99)) {
|
||||
this.G = nbttagcompound.getDouble("BorderSize");
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("BorderSizeLerpTime", 99)) {
|
||||
this.H = nbttagcompound.getLong("BorderSizeLerpTime");
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("BorderSizeLerpTarget", 99)) {
|
||||
this.I = nbttagcompound.getDouble("BorderSizeLerpTarget");
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("BorderSafeZone", 99)) {
|
||||
this.J = nbttagcompound.getDouble("BorderSafeZone");
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("BorderDamagePerBlock", 99)) {
|
||||
this.K = nbttagcompound.getDouble("BorderDamagePerBlock");
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("BorderWarningBlocks", 99)) {
|
||||
this.L = nbttagcompound.getInt("BorderWarningBlocks");
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("BorderWarningTime", 99)) {
|
||||
this.M = nbttagcompound.getInt("BorderWarningTime");
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("DimensionData", 10)) {
|
||||
nbttagcompound1 = nbttagcompound.getCompound("DimensionData");
|
||||
Iterator iterator = nbttagcompound1.c().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
String s1 = (String) iterator.next();
|
||||
|
||||
this.N.put(DimensionManager.a(Integer.parseInt(s1)), nbttagcompound1.getCompound(s1));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public WorldData(WorldSettings worldsettings, String s) {
|
||||
this.f = WorldType.NORMAL;
|
||||
this.g = "";
|
||||
this.G = 6.0E7D;
|
||||
this.J = 5.0D;
|
||||
this.K = 0.2D;
|
||||
this.L = 5;
|
||||
this.M = 15;
|
||||
this.N = Maps.newEnumMap(DimensionManager.class);
|
||||
this.O = new GameRules();
|
||||
this.a(worldsettings);
|
||||
this.levelName = s;
|
||||
this.C = WorldData.a;
|
||||
this.B = false;
|
||||
}
|
||||
|
||||
public void a(WorldSettings worldsettings) {
|
||||
this.e = worldsettings.d();
|
||||
this.x = worldsettings.e();
|
||||
this.y = worldsettings.g();
|
||||
this.z = worldsettings.f();
|
||||
this.f = worldsettings.h();
|
||||
this.g = worldsettings.j();
|
||||
this.A = worldsettings.i();
|
||||
}
|
||||
|
||||
public WorldData(WorldData worlddata) {
|
||||
this.f = WorldType.NORMAL;
|
||||
this.g = "";
|
||||
this.G = 6.0E7D;
|
||||
this.J = 5.0D;
|
||||
this.K = 0.2D;
|
||||
this.L = 5;
|
||||
this.M = 15;
|
||||
this.N = Maps.newEnumMap(DimensionManager.class);
|
||||
this.O = new GameRules();
|
||||
this.e = worlddata.e;
|
||||
this.f = worlddata.f;
|
||||
this.g = worlddata.g;
|
||||
this.x = worlddata.x;
|
||||
this.y = worlddata.y;
|
||||
this.h = worlddata.h;
|
||||
this.i = worlddata.i;
|
||||
this.j = worlddata.j;
|
||||
this.k = worlddata.k;
|
||||
this.l = worlddata.l;
|
||||
this.m = worlddata.m;
|
||||
this.n = worlddata.n;
|
||||
this.o = worlddata.o;
|
||||
this.p = worlddata.p;
|
||||
this.levelName = worlddata.levelName;
|
||||
this.r = worlddata.r;
|
||||
this.u = worlddata.u;
|
||||
this.t = worlddata.t;
|
||||
this.w = worlddata.w;
|
||||
this.v = worlddata.v;
|
||||
this.z = worlddata.z;
|
||||
this.A = worlddata.A;
|
||||
this.B = worlddata.B;
|
||||
this.O = worlddata.O;
|
||||
this.C = worlddata.C;
|
||||
this.D = worlddata.D;
|
||||
this.E = worlddata.E;
|
||||
this.F = worlddata.F;
|
||||
this.G = worlddata.G;
|
||||
this.H = worlddata.H;
|
||||
this.I = worlddata.I;
|
||||
this.J = worlddata.J;
|
||||
this.K = worlddata.K;
|
||||
this.M = worlddata.M;
|
||||
this.L = worlddata.L;
|
||||
}
|
||||
|
||||
public NBTTagCompound a(@Nullable NBTTagCompound nbttagcompound) {
|
||||
if (nbttagcompound == null) {
|
||||
nbttagcompound = this.o;
|
||||
}
|
||||
|
||||
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
|
||||
|
||||
this.a(nbttagcompound1, nbttagcompound);
|
||||
return nbttagcompound1;
|
||||
}
|
||||
|
||||
private void a(NBTTagCompound nbttagcompound, NBTTagCompound nbttagcompound1) {
|
||||
NBTTagCompound nbttagcompound2 = new NBTTagCompound();
|
||||
|
||||
nbttagcompound2.setString("Name", "1.12.2");
|
||||
nbttagcompound2.setInt("Id", 1343);
|
||||
nbttagcompound2.setBoolean("Snapshot", false);
|
||||
nbttagcompound.set("Version", nbttagcompound2);
|
||||
nbttagcompound.setInt("DataVersion", 1343);
|
||||
nbttagcompound.setLong("RandomSeed", this.e);
|
||||
nbttagcompound.setString("generatorName", this.f.name());
|
||||
nbttagcompound.setInt("generatorVersion", this.f.getVersion());
|
||||
nbttagcompound.setString("generatorOptions", this.g);
|
||||
nbttagcompound.setInt("GameType", this.x.getId());
|
||||
nbttagcompound.setBoolean("MapFeatures", this.y);
|
||||
nbttagcompound.setInt("SpawnX", this.h);
|
||||
nbttagcompound.setInt("SpawnY", this.i);
|
||||
nbttagcompound.setInt("SpawnZ", this.j);
|
||||
nbttagcompound.setLong("Time", this.k);
|
||||
nbttagcompound.setLong("DayTime", this.l);
|
||||
nbttagcompound.setLong("SizeOnDisk", this.n);
|
||||
nbttagcompound.setLong("LastPlayed", MinecraftServer.aw());
|
||||
nbttagcompound.setString("LevelName", this.levelName);
|
||||
nbttagcompound.setInt("version", this.r);
|
||||
nbttagcompound.setInt("clearWeatherTime", this.s);
|
||||
nbttagcompound.setInt("rainTime", this.u);
|
||||
nbttagcompound.setBoolean("raining", this.t);
|
||||
nbttagcompound.setInt("thunderTime", this.w);
|
||||
nbttagcompound.setBoolean("thundering", this.v);
|
||||
nbttagcompound.setBoolean("hardcore", this.z);
|
||||
nbttagcompound.setBoolean("allowCommands", this.A);
|
||||
nbttagcompound.setBoolean("initialized", this.B);
|
||||
nbttagcompound.setDouble("BorderCenterX", this.E);
|
||||
nbttagcompound.setDouble("BorderCenterZ", this.F);
|
||||
nbttagcompound.setDouble("BorderSize", this.G);
|
||||
nbttagcompound.setLong("BorderSizeLerpTime", this.H);
|
||||
nbttagcompound.setDouble("BorderSafeZone", this.J);
|
||||
nbttagcompound.setDouble("BorderDamagePerBlock", this.K);
|
||||
nbttagcompound.setDouble("BorderSizeLerpTarget", this.I);
|
||||
nbttagcompound.setDouble("BorderWarningBlocks", this.L);
|
||||
nbttagcompound.setDouble("BorderWarningTime", this.M);
|
||||
if (this.C != null) {
|
||||
nbttagcompound.setByte("Difficulty", (byte) this.C.a());
|
||||
}
|
||||
|
||||
nbttagcompound.setBoolean("DifficultyLocked", this.D);
|
||||
nbttagcompound.set("GameRules", this.O.a());
|
||||
NBTTagCompound nbttagcompound3 = new NBTTagCompound();
|
||||
Iterator iterator = this.N.entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry entry = (Entry) iterator.next();
|
||||
|
||||
nbttagcompound3.set(String.valueOf(((DimensionManager) entry.getKey()).getDimensionID()), (NBTBase) entry.getValue());
|
||||
}
|
||||
|
||||
nbttagcompound.set("DimensionData", nbttagcompound3);
|
||||
if (nbttagcompound1 != null) {
|
||||
nbttagcompound.set("Player", nbttagcompound1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public long getSeed() {
|
||||
return this.e;
|
||||
}
|
||||
|
||||
public int b() {
|
||||
return this.h;
|
||||
}
|
||||
|
||||
public int c() {
|
||||
return this.i;
|
||||
}
|
||||
|
||||
public int d() {
|
||||
return this.j;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return this.k;
|
||||
}
|
||||
|
||||
public long getDayTime() {
|
||||
return this.l;
|
||||
}
|
||||
|
||||
public NBTTagCompound h() {
|
||||
return this.o;
|
||||
}
|
||||
|
||||
public void setTime(long i) {
|
||||
this.k = i;
|
||||
}
|
||||
|
||||
public void setDayTime(long i) {
|
||||
this.l = i;
|
||||
}
|
||||
|
||||
public void setSpawn(BlockPosition blockposition) {
|
||||
this.h = blockposition.getX();
|
||||
this.i = blockposition.getY();
|
||||
this.j = blockposition.getZ();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.levelName;
|
||||
}
|
||||
|
||||
public void a(String s) {
|
||||
this.levelName = s;
|
||||
}
|
||||
|
||||
public int k() {
|
||||
return this.r;
|
||||
}
|
||||
|
||||
public void e(int i) {
|
||||
this.r = i;
|
||||
}
|
||||
|
||||
public int z() {
|
||||
return this.s;
|
||||
}
|
||||
|
||||
public void i(int i) {
|
||||
this.s = i;
|
||||
}
|
||||
|
||||
public boolean isThundering() {
|
||||
return this.v;
|
||||
}
|
||||
|
||||
public void setThundering(boolean flag) {
|
||||
// CraftBukkit start
|
||||
org.bukkit.World world = Bukkit.getWorld(getName());
|
||||
if (world != null) {
|
||||
ThunderChangeEvent thunder = new ThunderChangeEvent(world, flag);
|
||||
Bukkit.getServer().getPluginManager().callEvent(thunder);
|
||||
if (thunder.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.v = flag;
|
||||
}
|
||||
|
||||
public int getThunderDuration() {
|
||||
return this.w;
|
||||
}
|
||||
|
||||
public void setThunderDuration(int i) {
|
||||
this.w = i;
|
||||
}
|
||||
|
||||
public boolean hasStorm() {
|
||||
return this.t;
|
||||
}
|
||||
|
||||
public void setStorm(boolean flag) {
|
||||
// CraftBukkit start
|
||||
org.bukkit.World world = Bukkit.getWorld(getName());
|
||||
if (world != null) {
|
||||
WeatherChangeEvent weather = new WeatherChangeEvent(world, flag);
|
||||
Bukkit.getServer().getPluginManager().callEvent(weather);
|
||||
if (weather.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.t = flag;
|
||||
}
|
||||
|
||||
public int getWeatherDuration() {
|
||||
return this.u;
|
||||
}
|
||||
|
||||
public void setWeatherDuration(int i) {
|
||||
this.u = i;
|
||||
}
|
||||
|
||||
public EnumGamemode getGameType() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public boolean shouldGenerateMapFeatures() {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
public void f(boolean flag) {
|
||||
this.y = flag;
|
||||
}
|
||||
|
||||
public void setGameType(EnumGamemode enumgamemode) {
|
||||
this.x = enumgamemode;
|
||||
}
|
||||
|
||||
public boolean isHardcore() {
|
||||
return this.z;
|
||||
}
|
||||
|
||||
public void g(boolean flag) {
|
||||
this.z = flag;
|
||||
}
|
||||
|
||||
public WorldType getType() {
|
||||
return this.f;
|
||||
}
|
||||
|
||||
public void a(WorldType worldtype) {
|
||||
this.f = worldtype;
|
||||
}
|
||||
|
||||
public String getGeneratorOptions() {
|
||||
return this.g == null ? "" : this.g;
|
||||
}
|
||||
|
||||
public boolean u() {
|
||||
return this.A;
|
||||
}
|
||||
|
||||
public void c(boolean flag) {
|
||||
this.A = flag;
|
||||
}
|
||||
|
||||
public boolean v() {
|
||||
return this.B;
|
||||
}
|
||||
|
||||
public void d(boolean flag) {
|
||||
this.B = flag;
|
||||
}
|
||||
|
||||
public GameRules w() {
|
||||
return this.O;
|
||||
}
|
||||
|
||||
public double B() {
|
||||
return this.E;
|
||||
}
|
||||
|
||||
public double C() {
|
||||
return this.F;
|
||||
}
|
||||
|
||||
public double D() {
|
||||
return this.G;
|
||||
}
|
||||
|
||||
public void a(double d0) {
|
||||
this.G = d0;
|
||||
}
|
||||
|
||||
public long E() {
|
||||
return this.H;
|
||||
}
|
||||
|
||||
public void e(long i) {
|
||||
this.H = i;
|
||||
}
|
||||
|
||||
public double F() {
|
||||
return this.I;
|
||||
}
|
||||
|
||||
public void b(double d0) {
|
||||
this.I = d0;
|
||||
}
|
||||
|
||||
public void c(double d0) {
|
||||
this.F = d0;
|
||||
}
|
||||
|
||||
public void d(double d0) {
|
||||
this.E = d0;
|
||||
}
|
||||
|
||||
public double G() {
|
||||
return this.J;
|
||||
}
|
||||
|
||||
public void e(double d0) {
|
||||
this.J = d0;
|
||||
}
|
||||
|
||||
public double H() {
|
||||
return this.K;
|
||||
}
|
||||
|
||||
public void f(double d0) {
|
||||
this.K = d0;
|
||||
}
|
||||
|
||||
public int I() {
|
||||
return this.L;
|
||||
}
|
||||
|
||||
public int J() {
|
||||
return this.M;
|
||||
}
|
||||
|
||||
public void j(int i) {
|
||||
this.L = i;
|
||||
}
|
||||
|
||||
public void k(int i) {
|
||||
this.M = i;
|
||||
}
|
||||
|
||||
public EnumDifficulty getDifficulty() {
|
||||
return this.C;
|
||||
}
|
||||
|
||||
public void setDifficulty(EnumDifficulty enumdifficulty) {
|
||||
this.C = enumdifficulty;
|
||||
// CraftBukkit start
|
||||
PacketPlayOutServerDifficulty packet = new PacketPlayOutServerDifficulty(this.getDifficulty(), this.isDifficultyLocked());
|
||||
for (EntityPlayer player : (java.util.List<EntityPlayer>) (java.util.List) world.players) {
|
||||
player.playerConnection.sendPacket(packet);
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
public boolean isDifficultyLocked() {
|
||||
return this.D;
|
||||
}
|
||||
|
||||
public void e(boolean flag) {
|
||||
this.D = flag;
|
||||
}
|
||||
|
||||
public void a(CrashReportSystemDetails crashreportsystemdetails) {
|
||||
crashreportsystemdetails.a("Level seed", new CrashReportCallable() {
|
||||
public String a() throws Exception {
|
||||
return String.valueOf(WorldData.this.getSeed());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return this.a();
|
||||
}
|
||||
});
|
||||
crashreportsystemdetails.a("Level generator", new CrashReportCallable() {
|
||||
public String a() throws Exception {
|
||||
return String.format("ID %02d - %s, ver %d. Features enabled: %b", new Object[] { Integer.valueOf(WorldData.this.f.g()), WorldData.this.f.name(), Integer.valueOf(WorldData.this.f.getVersion()), Boolean.valueOf(WorldData.this.y)});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return this.a();
|
||||
}
|
||||
});
|
||||
crashreportsystemdetails.a("Level generator options", new CrashReportCallable() {
|
||||
public String a() throws Exception {
|
||||
return WorldData.this.g;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return this.a();
|
||||
}
|
||||
});
|
||||
crashreportsystemdetails.a("Level spawn location", new CrashReportCallable() {
|
||||
public String a() throws Exception {
|
||||
return CrashReportSystemDetails.a(WorldData.this.h, WorldData.this.i, WorldData.this.j);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return this.a();
|
||||
}
|
||||
});
|
||||
crashreportsystemdetails.a("Level time", new CrashReportCallable() {
|
||||
public String a() throws Exception {
|
||||
return String.format("%d game time, %d day time", new Object[] { Long.valueOf(WorldData.this.k), Long.valueOf(WorldData.this.l)});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return this.a();
|
||||
}
|
||||
});
|
||||
crashreportsystemdetails.a("Level dimension", new CrashReportCallable() {
|
||||
public String a() throws Exception {
|
||||
return String.valueOf(WorldData.this.p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return this.a();
|
||||
}
|
||||
});
|
||||
crashreportsystemdetails.a("Level storage version", new CrashReportCallable() {
|
||||
public String a() throws Exception {
|
||||
String s = "Unknown?";
|
||||
|
||||
try {
|
||||
switch (WorldData.this.r) {
|
||||
case 19132:
|
||||
s = "McRegion";
|
||||
break;
|
||||
|
||||
case 19133:
|
||||
s = "Anvil";
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
;
|
||||
}
|
||||
|
||||
return String.format("0x%05X - %s", new Object[] { Integer.valueOf(WorldData.this.r), s});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return this.a();
|
||||
}
|
||||
});
|
||||
crashreportsystemdetails.a("Level weather", new CrashReportCallable() {
|
||||
public String a() throws Exception {
|
||||
return String.format("Rain time: %d (now: %b), thunder time: %d (now: %b)", new Object[] { Integer.valueOf(WorldData.this.u), Boolean.valueOf(WorldData.this.t), Integer.valueOf(WorldData.this.w), Boolean.valueOf(WorldData.this.v)});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return this.a();
|
||||
}
|
||||
});
|
||||
crashreportsystemdetails.a("Level game mode", new CrashReportCallable() {
|
||||
public String a() throws Exception {
|
||||
return String.format("Game mode: %s (ID %d). Hardcore: %b. Cheats: %b", new Object[] { WorldData.this.x.b(), Integer.valueOf(WorldData.this.x.getId()), Boolean.valueOf(WorldData.this.z), Boolean.valueOf(WorldData.this.A)});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return this.a();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public NBTTagCompound a(DimensionManager dimensionmanager) {
|
||||
NBTTagCompound nbttagcompound = this.N.get(dimensionmanager);
|
||||
|
||||
return nbttagcompound == null ? new NBTTagCompound() : nbttagcompound;
|
||||
}
|
||||
|
||||
public void a(DimensionManager dimensionmanager, NBTTagCompound nbttagcompound) {
|
||||
this.N.put(dimensionmanager, nbttagcompound);
|
||||
}
|
||||
|
||||
// CraftBukkit start - Check if the name stored in NBT is the correct one
|
||||
public void checkName( String name ) {
|
||||
if ( !this.levelName.equals( name ) ) {
|
||||
this.levelName = name;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -17,6 +18,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -35,6 +37,7 @@ import org.bukkit.Server;
|
||||
import org.bukkit.UnsafeValues;
|
||||
import org.bukkit.Warning.WarningState;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.StructureType;
|
||||
import org.bukkit.World.Environment;
|
||||
import org.bukkit.WorldCreator;
|
||||
import org.bukkit.boss.BarColor;
|
||||
@@ -52,12 +55,12 @@ import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerialization;
|
||||
import org.bukkit.conversations.Conversable;
|
||||
import org.bukkit.craftbukkit.boss.CraftBossBar;
|
||||
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.generator.CraftChunkData;
|
||||
import org.bukkit.craftbukkit.help.SimpleHelpMap;
|
||||
import org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe;
|
||||
import org.bukkit.craftbukkit.inventory.CraftInventoryCustom;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemFactory;
|
||||
import org.bukkit.craftbukkit.inventory.CraftMerchantCustom;
|
||||
import org.bukkit.craftbukkit.inventory.CraftRecipe;
|
||||
@@ -95,6 +98,8 @@ import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.Recipe;
|
||||
import org.bukkit.inventory.ShapedRecipe;
|
||||
import org.bukkit.inventory.ShapelessRecipe;
|
||||
import org.bukkit.loot.LootTable;
|
||||
import org.bukkit.map.MapView;
|
||||
import org.bukkit.permissions.Permissible;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
@@ -123,33 +128,53 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufOutputStream;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.base64.Base64;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import org.bukkit.Keyed;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.command.BukkitCommandWrapper;
|
||||
import org.bukkit.craftbukkit.command.CraftCommandMap;
|
||||
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
|
||||
import org.bukkit.craftbukkit.inventory.util.CraftInventoryCreator;
|
||||
import org.bukkit.craftbukkit.tag.CraftBlockTag;
|
||||
import org.bukkit.craftbukkit.tag.CraftItemTag;
|
||||
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
||||
import org.bukkit.event.server.ServerLoadEvent;
|
||||
import org.bukkit.event.server.TabCompleteEvent;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
import javax.annotation.Nullable; // Paper
|
||||
import javax.annotation.Nonnull; // Paper
|
||||
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Guava -> Caffeine (performance)
|
||||
*/
|
||||
public final class CraftServer implements Server {
|
||||
private final String serverName = "Paper";
|
||||
private final String serverName = "Paper"; // Paper
|
||||
private final String serverVersion;
|
||||
private final String bukkitVersion = Versioning.getBukkitVersion();
|
||||
private final Logger logger = Logger.getLogger("Minecraft");
|
||||
private final ServicesManager servicesManager = new SimpleServicesManager();
|
||||
private final CraftScheduler scheduler = new CraftScheduler();
|
||||
private final SimpleCommandMap commandMap = new SimpleCommandMap(this);
|
||||
private final CraftCommandMap commandMap = new CraftCommandMap(this);
|
||||
private final SimpleHelpMap helpMap = new SimpleHelpMap(this);
|
||||
private final StandardMessenger messenger = new StandardMessenger();
|
||||
private final SimplePluginManager pluginManager = new SimplePluginManager(this, commandMap);
|
||||
@@ -177,8 +202,7 @@ public final class CraftServer implements Server {
|
||||
private boolean printSaveWarning;
|
||||
private CraftIconCache icon;
|
||||
private boolean overrideAllCommandBlockCommands = false;
|
||||
private boolean unrestrictedAdvancements;
|
||||
private boolean unrestrictedSignCommands; // Paper
|
||||
public boolean ignoreVanillaPermissions = false;
|
||||
private final List<CraftPlayer> playerView;
|
||||
public int reloadCount;
|
||||
public static Exception excessiveVelEx; // Paper - Velocity warnings
|
||||
@@ -256,13 +280,7 @@ public final class CraftServer implements Server {
|
||||
|
||||
saveCommandsConfig();
|
||||
overrideAllCommandBlockCommands = commandsConfiguration.getStringList("command-block-overrides").contains("*");
|
||||
unrestrictedAdvancements = commandsConfiguration.getBoolean("unrestricted-advancements");
|
||||
// Paper start
|
||||
unrestrictedSignCommands = commandsConfiguration.getBoolean("unrestricted-signs");
|
||||
if (unrestrictedSignCommands) {
|
||||
logger.warning("Warning: Commands are no longer restricted on signs. If you allow players to use Creative Mode, there may be risk of players bypassing permissions. Use this setting at your own risk!!!!");
|
||||
}
|
||||
// Paper end
|
||||
ignoreVanillaPermissions = commandsConfiguration.getBoolean("ignore-vanilla-permissions");
|
||||
pluginManager.useTimings(configuration.getBoolean("settings.plugin-profiling"));
|
||||
monsterSpawn = configuration.getInt("spawn-limits.monsters");
|
||||
animalSpawn = configuration.getInt("spawn-limits.animals");
|
||||
@@ -275,15 +293,6 @@ public final class CraftServer implements Server {
|
||||
loadIcon();
|
||||
}
|
||||
|
||||
public boolean getPermissionOverride(ICommandListener listener) {
|
||||
while (listener instanceof CommandListenerWrapper) {
|
||||
listener = ((CommandListenerWrapper) listener).base;
|
||||
}
|
||||
|
||||
if (unrestrictedSignCommands && listener instanceof TileEntitySign.ISignCommandListener) return true; // Paper
|
||||
return unrestrictedAdvancements && listener instanceof AdvancementRewards.AdvancementCommandListener;
|
||||
}
|
||||
|
||||
public boolean getCommandBlockOverride(String command) {
|
||||
return overrideAllCommandBlockCommands || commandsConfiguration.getStringList("command-block-overrides").contains(command);
|
||||
}
|
||||
@@ -359,6 +368,7 @@ public final class CraftServer implements Server {
|
||||
DefaultPermissions.registerCorePermissions();
|
||||
CraftDefaultPermissions.registerCorePermissions();
|
||||
helpMap.initializeCommands();
|
||||
syncCommands();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,10 +377,12 @@ public final class CraftServer implements Server {
|
||||
}
|
||||
|
||||
private void setVanillaCommands(boolean first) { // Spigot
|
||||
Map<String, ICommand> commands = new CommandDispatcher(console).getCommands();
|
||||
for (ICommand cmd : commands.values()) {
|
||||
CommandDispatcher dispatcher = console.vanillaCommandDispatcher;
|
||||
|
||||
// Build a list of all Vanilla commands and create wrappers
|
||||
for (CommandNode<CommandListenerWrapper> cmd : dispatcher.a().getRoot().getChildren()) {
|
||||
// Spigot start
|
||||
VanillaCommandWrapper wrapper = new VanillaCommandWrapper((CommandAbstract) cmd, LocaleI18n.get(cmd.getUsage(null)));
|
||||
VanillaCommandWrapper wrapper = new VanillaCommandWrapper(dispatcher, cmd);
|
||||
if (org.spigotmc.SpigotConfig.replaceCommands.contains( wrapper.getName() ) ) {
|
||||
if (first) {
|
||||
commandMap.register("minecraft", wrapper);
|
||||
@@ -382,6 +394,38 @@ public final class CraftServer implements Server {
|
||||
}
|
||||
}
|
||||
|
||||
private void syncCommands() {
|
||||
// Clear existing commands
|
||||
CommandDispatcher dispatcher = console.commandDispatcher = new CommandDispatcher();
|
||||
|
||||
// Register all commands, vanilla ones will be using the old dispatcher references
|
||||
for (Map.Entry<String, Command> entry : commandMap.getKnownCommands().entrySet()) {
|
||||
String label = entry.getKey();
|
||||
Command command = entry.getValue();
|
||||
|
||||
if (command instanceof VanillaCommandWrapper) {
|
||||
LiteralCommandNode<CommandListenerWrapper> node = (LiteralCommandNode<CommandListenerWrapper>) ((VanillaCommandWrapper) command).vanillaCommand;
|
||||
if (!node.getLiteral().equals(label)) {
|
||||
LiteralCommandNode<CommandListenerWrapper> clone = new LiteralCommandNode(label, node.getCommand(), node.getRequirement(), node.getRedirect(), node.getRedirectModifier(), node.isFork());
|
||||
|
||||
for (CommandNode<CommandListenerWrapper> child : node.getChildren()) {
|
||||
clone.addChild(child);
|
||||
}
|
||||
node = clone;
|
||||
}
|
||||
|
||||
dispatcher.a().getRoot().addChild(node);
|
||||
} else {
|
||||
new BukkitCommandWrapper(this, entry.getValue()).register(dispatcher.a(), label);
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh commands
|
||||
for (EntityPlayer player : getHandle().players) {
|
||||
dispatcher.a(player);
|
||||
}
|
||||
}
|
||||
|
||||
private void enablePlugin(Plugin plugin) {
|
||||
try {
|
||||
List<Permission> perms = plugin.getDescription().getPermissions();
|
||||
@@ -661,6 +705,7 @@ public final class CraftServer implements Server {
|
||||
public boolean dispatchCommand(CommandSender sender, String commandLine) {
|
||||
Validate.notNull(sender, "Sender cannot be null");
|
||||
Validate.notNull(commandLine, "CommandLine cannot be null");
|
||||
org.spigotmc.AsyncCatcher.catchOp( "command dispatch" ); // Spigot
|
||||
|
||||
// Paper Start
|
||||
if (!org.spigotmc.AsyncCatcher.shuttingDown && !Bukkit.isPrimaryThread()) {
|
||||
@@ -690,7 +735,7 @@ public final class CraftServer implements Server {
|
||||
}
|
||||
|
||||
// Spigot start
|
||||
if (StringUtils.isNotEmpty(org.spigotmc.SpigotConfig.unknownCommandMessage)) {
|
||||
if (!org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty()) {
|
||||
// Paper start
|
||||
UnknownCommandEvent event = new UnknownCommandEvent(sender, commandLine, org.spigotmc.SpigotConfig.unknownCommandMessage);
|
||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
@@ -714,8 +759,8 @@ public final class CraftServer implements Server {
|
||||
((DedicatedServer) console).propertyManager = config;
|
||||
|
||||
boolean animals = config.getBoolean("spawn-animals", console.getSpawnAnimals());
|
||||
boolean monsters = config.getBoolean("spawn-monsters", console.worlds.get(0).getDifficulty() != EnumDifficulty.PEACEFUL);
|
||||
EnumDifficulty difficulty = EnumDifficulty.getById(config.getInt("difficulty", console.worlds.get(0).getDifficulty().ordinal()));
|
||||
boolean monsters = config.getBoolean("spawn-monsters", console.getWorldServer(DimensionManager.OVERWORLD).getDifficulty() != EnumDifficulty.PEACEFUL);
|
||||
EnumDifficulty difficulty = EnumDifficulty.getById(config.getInt("difficulty", console.getWorldServer(DimensionManager.OVERWORLD).getDifficulty().ordinal()));
|
||||
|
||||
online.value = config.getBoolean("online-mode", console.getOnlineMode());
|
||||
console.setSpawnAnimals(config.getBoolean("spawn-animals", console.getSpawnAnimals()));
|
||||
@@ -746,7 +791,7 @@ public final class CraftServer implements Server {
|
||||
|
||||
org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot
|
||||
com.destroystokyo.paper.PaperConfig.init((File) console.options.valueOf("paper-settings")); // Paper
|
||||
for (WorldServer world : console.worlds) {
|
||||
for (WorldServer world : console.getWorlds()) {
|
||||
world.worldData.setDifficulty(difficulty);
|
||||
world.setSpawnFlags(monsters, animals);
|
||||
if (this.getTicksPerAnimalSpawns() < 0) {
|
||||
@@ -781,6 +826,7 @@ public final class CraftServer implements Server {
|
||||
org.spigotmc.SpigotConfig.registerCommands(); // Spigot
|
||||
com.destroystokyo.paper.PaperConfig.registerCommands(); // Paper
|
||||
overrideAllCommandBlockCommands = commandsConfiguration.getStringList("command-block-overrides").contains("*");
|
||||
ignoreVanillaPermissions = commandsConfiguration.getBoolean("ignore-vanilla-permissions");
|
||||
|
||||
int pollCount = 0;
|
||||
|
||||
@@ -809,6 +855,7 @@ public final class CraftServer implements Server {
|
||||
loadPlugins();
|
||||
enablePlugins(PluginLoadOrder.STARTUP);
|
||||
enablePlugins(PluginLoadOrder.POSTWORLD);
|
||||
getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.RELOAD));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -919,31 +966,13 @@ public final class CraftServer implements Server {
|
||||
generator = getGenerator(name);
|
||||
}
|
||||
|
||||
Convertable converter = new WorldLoaderServer(getWorldContainer(), getHandle().getServer().dataConverterManager);
|
||||
if (converter.isConvertable(name)) {
|
||||
getLogger().info("Converting world '" + name + "'");
|
||||
converter.convert(name, new IProgressUpdate() {
|
||||
private long b = System.currentTimeMillis();
|
||||
console.convertWorld(name);
|
||||
|
||||
public void a(String s) {}
|
||||
|
||||
public void a(int i) {
|
||||
if (System.currentTimeMillis() - this.b >= 1000L) {
|
||||
this.b = System.currentTimeMillis();
|
||||
MinecraftServer.LOGGER.info("Converting... " + i + "%");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void c(String s) {}
|
||||
});
|
||||
}
|
||||
|
||||
int dimension = CraftWorld.CUSTOM_DIMENSION_OFFSET + console.worlds.size();
|
||||
int dimension = CraftWorld.CUSTOM_DIMENSION_OFFSET + console.worldServer.size();
|
||||
boolean used = false;
|
||||
do {
|
||||
for (WorldServer server : console.worlds) {
|
||||
used = server.dimension == dimension;
|
||||
for (WorldServer server : console.getWorlds()) {
|
||||
used = server.dimension.getDimensionID() + 1 == dimension; // Paper - getDimensionID returns the dimension - 1, so we have to add 1
|
||||
if (used) {
|
||||
dimension++;
|
||||
break;
|
||||
@@ -952,16 +981,22 @@ public final class CraftServer implements Server {
|
||||
} while(used);
|
||||
boolean hardcore = false;
|
||||
|
||||
IDataManager sdm = new ServerNBTManager(getWorldContainer(), name, true, getHandle().getServer().dataConverterManager);
|
||||
IDataManager sdm = new ServerNBTManager(getWorldContainer(), name, getServer(), getHandle().getServer().dataConverterManager);
|
||||
PersistentCollection persistentcollection = new PersistentCollection(sdm);
|
||||
WorldData worlddata = sdm.getWorldData();
|
||||
WorldSettings worldSettings = null;
|
||||
if (worlddata == null) {
|
||||
worldSettings = new WorldSettings(creator.seed(), EnumGamemode.getById(getDefaultGameMode().getValue()), generateStructures, hardcore, type);
|
||||
worldSettings.setGeneratorSettings(creator.generatorSettings());
|
||||
worldSettings = new WorldSettings(com.destroystokyo.paper.PaperConfig.seedOverride.getOrDefault(name, creator.seed()), EnumGamemode.getById(getDefaultGameMode().getValue()), generateStructures, hardcore, type); // Paper
|
||||
JsonElement parsedSettings = new JsonParser().parse(creator.generatorSettings());
|
||||
if (parsedSettings.isJsonObject()) {
|
||||
worldSettings.setGeneratorSettings(parsedSettings.getAsJsonObject());
|
||||
}
|
||||
worlddata = new WorldData(worldSettings, name);
|
||||
}
|
||||
worlddata.checkName(name); // CraftBukkit - Migration did not rewrite the level.dat; This forces 1.8 to take the last loaded world as respawn (in this case the end)
|
||||
WorldServer internal = (WorldServer) new WorldServer(console, sdm, worlddata, dimension, console.methodProfiler, creator.environment(), generator).b();
|
||||
|
||||
DimensionManager internalDimension = new DimensionManager(dimension, name, name, () -> DimensionManager.a(creator.environment().getId()).e());
|
||||
WorldServer internal = (WorldServer) new WorldServer(console, sdm, persistentcollection, worlddata, internalDimension, console.methodProfiler, creator.environment(), generator).i_();
|
||||
|
||||
if (!(worlds.containsKey(name.toLowerCase(java.util.Locale.ENGLISH)))) {
|
||||
return null;
|
||||
@@ -970,22 +1005,25 @@ public final class CraftServer implements Server {
|
||||
if (worldSettings != null) {
|
||||
internal.a(worldSettings);
|
||||
}
|
||||
internal.scoreboard = getScoreboardManager().getMainScoreboard().getHandle();
|
||||
|
||||
internal.tracker = new EntityTracker(internal);
|
||||
internal.addIWorldAccess(new WorldManager(console, internal));
|
||||
internal.worldData.setDifficulty(EnumDifficulty.EASY);
|
||||
internal.setSpawnFlags(true, true);
|
||||
console.worlds.add(internal);
|
||||
console.worldServer.put(internal.dimension, internal);
|
||||
|
||||
pluginManager.callEvent(new WorldInitEvent(internal.getWorld()));
|
||||
System.out.println("Preparing start region for level " + (console.worlds.size() - 1) + " (Seed: " + internal.getSeed() + ")");
|
||||
System.out.println("Preparing start region for level " + (console.worldServer.size() - 1) + " (Seed: " + internal.getSeed() + ")");
|
||||
|
||||
if (internal.getWorld().getKeepSpawnInMemory()) {
|
||||
short short1 = internal.paperConfig.keepLoadedRange; // Paper
|
||||
long i = System.currentTimeMillis();
|
||||
for (int j = -short1; j <= short1; j += 16) {
|
||||
for (int k = -short1; k <= short1; k += 16) {
|
||||
// Paper start
|
||||
for (ChunkCoordIntPair coords : internal.getChunkProviderServer().getSpiralOutChunks(internal.getSpawn(), short1 >> 4)) {{
|
||||
int j = coords.x;
|
||||
int k = coords.z;
|
||||
// Paper end
|
||||
|
||||
long l = System.currentTimeMillis();
|
||||
|
||||
if (l < i) {
|
||||
@@ -1001,10 +1039,26 @@ public final class CraftServer implements Server {
|
||||
}
|
||||
|
||||
BlockPosition chunkcoordinates = internal.getSpawn();
|
||||
internal.getChunkProviderServer().getChunkAt(chunkcoordinates.getX() + j >> 4, chunkcoordinates.getZ() + k >> 4);
|
||||
internal.getChunkProviderServer().getChunkAt(chunkcoordinates.getX() + j >> 4, chunkcoordinates.getZ() + k >> 4, true, true, c -> {}); // Paper
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DimensionManager dimensionmanager = internalDimension.e().getDimensionManager();
|
||||
ForcedChunk forcedchunk = (ForcedChunk) persistentcollection.get(dimensionmanager, ForcedChunk::new, "chunks");
|
||||
|
||||
if (forcedchunk != null) {
|
||||
LongIterator longiterator = forcedchunk.a().iterator();
|
||||
|
||||
while (longiterator.hasNext()) {
|
||||
System.out.println("Loading forced chunks for dimension " + dimension + ", " + forcedchunk.a().size() * 100 / 625 + "%");
|
||||
long k = longiterator.nextLong();
|
||||
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(k);
|
||||
|
||||
internal.getChunkProviderServer().getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
pluginManager.callEvent(new WorldLoadEvent(internal.getWorld()));
|
||||
return internal.getWorld();
|
||||
}
|
||||
@@ -1022,11 +1076,11 @@ public final class CraftServer implements Server {
|
||||
|
||||
WorldServer handle = ((CraftWorld) world).getHandle();
|
||||
|
||||
if (!(console.worlds.contains(handle))) {
|
||||
if (!(console.worldServer.containsKey(handle.dimension))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (handle.dimension == 0) {
|
||||
if (handle.dimension == DimensionManager.OVERWORLD) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1044,14 +1098,14 @@ public final class CraftServer implements Server {
|
||||
if (save) {
|
||||
try {
|
||||
handle.save(true, null);
|
||||
handle.saveLevel();
|
||||
handle.close();
|
||||
} catch (ExceptionWorldConflict ex) {
|
||||
getLogger().log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
worlds.remove(world.getName().toLowerCase(java.util.Locale.ENGLISH));
|
||||
console.worlds.remove(console.worlds.indexOf(handle));
|
||||
console.worldServer.remove(handle.dimension);
|
||||
|
||||
File parentFolder = world.getWorldFolder().getAbsoluteFile();
|
||||
|
||||
@@ -1186,19 +1240,12 @@ public final class CraftServer implements Server {
|
||||
|
||||
@Override
|
||||
public void clearRecipes() {
|
||||
CraftingManager.recipes = new RegistryMaterials();
|
||||
RecipesFurnace.getInstance().recipes.clear();
|
||||
RecipesFurnace.getInstance().customRecipes.clear();
|
||||
RecipesFurnace.getInstance().customExperience.clear();
|
||||
console.getCraftingManager().recipes.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetRecipes() {
|
||||
CraftingManager.recipes = new RegistryMaterials();
|
||||
CraftingManager.init();
|
||||
RecipesFurnace.getInstance().recipes = new RecipesFurnace().recipes;
|
||||
RecipesFurnace.getInstance().customRecipes.clear();
|
||||
RecipesFurnace.getInstance().customExperience.clear();
|
||||
console.getCraftingManager().a(console.getResourceManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1302,8 +1349,8 @@ public final class CraftServer implements Server {
|
||||
@Override
|
||||
@Deprecated
|
||||
public CraftMapView getMap(short id) {
|
||||
PersistentCollection collection = console.worlds.get(0).worldMaps;
|
||||
WorldMap worldmap = (WorldMap) collection.get(WorldMap.class, "map_" + id);
|
||||
PersistentCollection collection = console.getWorldServer(DimensionManager.OVERWORLD).worldMaps;
|
||||
WorldMap worldmap = (WorldMap) collection.get(DimensionManager.OVERWORLD, WorldMap::new, "map_" + id);
|
||||
if (worldmap == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -1314,11 +1361,35 @@ public final class CraftServer implements Server {
|
||||
public CraftMapView createMap(World world) {
|
||||
Validate.notNull(world, "World cannot be null");
|
||||
|
||||
net.minecraft.server.ItemStack stack = new net.minecraft.server.ItemStack(Items.MAP, 1, -1);
|
||||
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(stack, ((CraftWorld) world).getHandle());
|
||||
net.minecraft.server.ItemStack stack = new net.minecraft.server.ItemStack(Items.MAP, 1);
|
||||
WorldMap worldmap = ItemWorldMap.getSavedMap(stack, ((CraftWorld) world).getHandle());
|
||||
return worldmap.mapView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack createExplorerMap(World world, Location location, StructureType structureType) {
|
||||
return this.createExplorerMap(world, location, structureType, 100, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack createExplorerMap(World world, Location location, StructureType structureType, int radius, boolean findUnexplored) {
|
||||
Validate.notNull(world, "World cannot be null");
|
||||
Validate.notNull(structureType, "StructureType cannot be null");
|
||||
Validate.notNull(structureType.getMapIcon(), "Cannot create explorer maps for StructureType " + structureType.getName());
|
||||
|
||||
WorldServer worldServer = ((CraftWorld) world).getHandle();
|
||||
Location structureLocation = world.locateNearestStructure(location, structureType, radius, findUnexplored);
|
||||
BlockPosition structurePosition = new BlockPosition(structureLocation.getBlockX(), structureLocation.getBlockY(), structureLocation.getBlockZ());
|
||||
|
||||
// Create map with trackPlayer = true, unlimitedTracking = true
|
||||
net.minecraft.server.ItemStack stack = ItemWorldMap.a(worldServer, structurePosition.getX(), structurePosition.getZ(), MapView.Scale.NORMAL.getValue(), true, true); //PAIL rename setFilledMapView
|
||||
ItemWorldMap.a(worldServer, stack); // PAIL rename sepiaMapFilter
|
||||
// "+" map ID taken from EntityVillager
|
||||
ItemWorldMap.getSavedMap(stack, worldServer).a(stack, structurePosition, "+", MapIcon.Type.a(structureType.getMapIcon().getValue())); // PAIL rename decorateMap
|
||||
|
||||
return CraftItemStack.asBukkitCopy(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
console.safeShutdown();
|
||||
@@ -1503,7 +1574,7 @@ public final class CraftServer implements Server {
|
||||
|
||||
@Override
|
||||
public GameMode getDefaultGameMode() {
|
||||
return GameMode.getByValue(console.worlds.get(0).getWorldData().getGameType().getId());
|
||||
return GameMode.getByValue(console.getWorldServer(DimensionManager.OVERWORLD).getWorldData().getGameType().getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1547,7 +1618,7 @@ public final class CraftServer implements Server {
|
||||
|
||||
@Override
|
||||
public OfflinePlayer[] getOfflinePlayers() {
|
||||
WorldNBTStorage storage = (WorldNBTStorage) console.worlds.get(0).getDataManager();
|
||||
WorldNBTStorage storage = (WorldNBTStorage) console.getWorldServer(DimensionManager.OVERWORLD).getDataManager();
|
||||
String[] files = storage.getPlayerDir().list(new DatFileFilter());
|
||||
Set<OfflinePlayer> players = new HashSet<OfflinePlayer>();
|
||||
|
||||
@@ -1591,25 +1662,26 @@ public final class CraftServer implements Server {
|
||||
|
||||
@Override
|
||||
public Inventory createInventory(InventoryHolder owner, InventoryType type) {
|
||||
// TODO: Create the appropriate type, rather than Custom?
|
||||
return new CraftInventoryCustom(owner, type);
|
||||
Validate.isTrue(type.isCreatable(), "Cannot open an inventory of type ", type);
|
||||
return CraftInventoryCreator.INSTANCE.createInventory(owner, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory createInventory(InventoryHolder owner, InventoryType type, String title) {
|
||||
return new CraftInventoryCustom(owner, type, title);
|
||||
Validate.isTrue(type.isCreatable(), "Cannot open an inventory of type ", type);
|
||||
return CraftInventoryCreator.INSTANCE.createInventory(owner, type, title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory createInventory(InventoryHolder owner, int size) throws IllegalArgumentException {
|
||||
Validate.isTrue(size % 9 == 0, "Chests must have a size that is a multiple of 9!");
|
||||
return new CraftInventoryCustom(owner, size);
|
||||
return CraftInventoryCreator.INSTANCE.createInventory(owner, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory createInventory(InventoryHolder owner, int size, String title) throws IllegalArgumentException {
|
||||
Validate.isTrue(size % 9 == 0, "Chests must have a size that is a multiple of 9!");
|
||||
return new CraftInventoryCustom(owner, size, title);
|
||||
return CraftInventoryCreator.INSTANCE.createInventory(owner, size, title);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1662,26 +1734,26 @@ public final class CraftServer implements Server {
|
||||
return warningState;
|
||||
}
|
||||
|
||||
public List<String> tabComplete(net.minecraft.server.ICommandListener sender, String message, BlockPosition pos, boolean forceCommand) {
|
||||
if (!(sender instanceof EntityPlayer)) {
|
||||
public List<String> tabComplete(CommandSender sender, String message, WorldServer world, Vec3D pos, boolean forceCommand) {
|
||||
if (!(sender instanceof Player)) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
List<String> offers;
|
||||
Player player = ((EntityPlayer) sender).getBukkitEntity();
|
||||
Player player = (Player) sender;
|
||||
if (message.startsWith("/") || forceCommand) {
|
||||
offers = tabCompleteCommand(player, message, pos);
|
||||
offers = tabCompleteCommand(player, message, world, pos);
|
||||
} else {
|
||||
offers = tabCompleteChat(player, message);
|
||||
}
|
||||
|
||||
TabCompleteEvent tabEvent = new TabCompleteEvent(player, message, offers, message.startsWith("/") || forceCommand, pos != null ? MCUtil.toLocation(((CraftWorld) player.getWorld()).getHandle(), pos) : null); // Paper
|
||||
|
||||
TabCompleteEvent tabEvent = new TabCompleteEvent(player, message, offers, message.startsWith("/") || forceCommand, pos != null ? MCUtil.toLocation(((CraftWorld) player.getWorld()).getHandle(), new BlockPosition(pos)) : null); // Paper
|
||||
getPluginManager().callEvent(tabEvent);
|
||||
|
||||
return tabEvent.isCancelled() ? Collections.EMPTY_LIST : tabEvent.getCompletions();
|
||||
}
|
||||
|
||||
public List<String> tabCompleteCommand(Player player, String message, BlockPosition pos) {
|
||||
public List<String> tabCompleteCommand(Player player, String message, WorldServer world, Vec3D pos) {
|
||||
// Spigot Start
|
||||
if ( (org.spigotmc.SpigotConfig.tabComplete < 0 || message.length() <= org.spigotmc.SpigotConfig.tabComplete) && !message.contains( " " ) )
|
||||
{
|
||||
@@ -1698,7 +1770,7 @@ public final class CraftServer implements Server {
|
||||
if (pos == null) {
|
||||
completions = getCommandMap().tabComplete(player, message);
|
||||
} else {
|
||||
completions = getCommandMap().tabComplete(player, message, new Location(player.getWorld(), pos.getX(), pos.getY(), pos.getZ()));
|
||||
completions = getCommandMap().tabComplete(player, message, new Location(world.getWorld(), pos.x, pos.y, pos.z));
|
||||
}
|
||||
} catch (CommandException ex) {
|
||||
player.sendMessage(ChatColor.RED + "An internal error occurred while attempting to tab-complete this command");
|
||||
@@ -1779,9 +1851,9 @@ public final class CraftServer implements Server {
|
||||
Validate.isTrue(image.getWidth() == 64, "Must be 64 pixels wide");
|
||||
Validate.isTrue(image.getHeight() == 64, "Must be 64 pixels high");
|
||||
ImageIO.write(image, "PNG", new ByteBufOutputStream(bytebuf));
|
||||
ByteBuf bytebuf1 = Base64.encode(bytebuf);
|
||||
ByteBuffer bytebuffer = Base64.getEncoder().encode(bytebuf.nioBuffer());
|
||||
|
||||
return new CraftIconCache("data:image/png;base64," + bytebuf1.toString(Charsets.UTF_8));
|
||||
return new CraftIconCache("data:image/png;base64," + StandardCharsets.UTF_8.decode(bytebuffer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1796,6 +1868,7 @@ public final class CraftServer implements Server {
|
||||
|
||||
@Override
|
||||
public ChunkGenerator.ChunkData createChunkData(World world) {
|
||||
Validate.notNull(world, "World cannot be null");
|
||||
return new CraftChunkData(world);
|
||||
}
|
||||
|
||||
@@ -1807,8 +1880,15 @@ public final class CraftServer implements Server {
|
||||
@Override
|
||||
public Entity getEntity(UUID uuid) {
|
||||
Validate.notNull(uuid, "UUID cannot be null");
|
||||
net.minecraft.server.Entity entity = console.a(uuid); // PAIL: getEntity
|
||||
return entity == null ? null : entity.getBukkitEntity();
|
||||
|
||||
for (WorldServer world : getServer().getWorlds()) {
|
||||
net.minecraft.server.Entity entity = world.getEntity(uuid);
|
||||
if (entity != null) {
|
||||
return entity.getBukkitEntity();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1821,7 +1901,7 @@ public final class CraftServer implements Server {
|
||||
|
||||
@Override
|
||||
public Iterator<org.bukkit.advancement.Advancement> advancementIterator() {
|
||||
return Iterators.unmodifiableIterator(Iterators.transform(console.getAdvancementData().c().iterator(), new Function<Advancement, org.bukkit.advancement.Advancement>() { // PAIL: rename
|
||||
return Iterators.unmodifiableIterator(Iterators.transform(console.getAdvancementData().b().iterator(), new Function<Advancement, org.bukkit.advancement.Advancement>() { // PAIL: rename
|
||||
@Override
|
||||
public org.bukkit.advancement.Advancement apply(Advancement advancement) {
|
||||
return advancement.bukkit;
|
||||
@@ -1829,6 +1909,64 @@ public final class CraftServer implements Server {
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData createBlockData(org.bukkit.Material material) {
|
||||
Validate.isTrue(material != null, "Must provide material");
|
||||
|
||||
return createBlockData(material, (String) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData createBlockData(org.bukkit.Material material, Consumer<BlockData> consumer) {
|
||||
BlockData data = createBlockData(material);
|
||||
|
||||
if (consumer != null) {
|
||||
consumer.accept(data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData createBlockData(String data) throws IllegalArgumentException {
|
||||
Validate.isTrue(data != null, "Must provide data");
|
||||
|
||||
return createBlockData(null, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData createBlockData(org.bukkit.Material material, String data) {
|
||||
Validate.isTrue(material != null || data != null, "Must provide one of material or data");
|
||||
|
||||
return CraftBlockData.newData(material, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Keyed> org.bukkit.Tag<T> getTag(String registry, NamespacedKey tag, Class<T> clazz) {
|
||||
MinecraftKey key = CraftNamespacedKey.toMinecraft(tag);
|
||||
|
||||
switch (registry) {
|
||||
case org.bukkit.Tag.REGISTRY_BLOCKS:
|
||||
Preconditions.checkArgument(clazz == org.bukkit.Material.class, "Block namespace must have material type");
|
||||
|
||||
return (org.bukkit.Tag<T>) new CraftBlockTag(console.getTagRegistry().a(), key);
|
||||
case org.bukkit.Tag.REGISTRY_ITEMS:
|
||||
Preconditions.checkArgument(clazz == org.bukkit.Material.class, "Item namespace must have material type");
|
||||
|
||||
return (org.bukkit.Tag<T>) new CraftItemTag(console.getTagRegistry().b(), key);
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
public LootTable getLootTable(NamespacedKey key) {
|
||||
Validate.notNull(key, "NamespacedKey cannot be null");
|
||||
|
||||
LootTableRegistry registry = getServer().getLootTableRegistry();
|
||||
return new CraftLootTable(key, registry.getLootTable(CraftNamespacedKey.toMinecraft(key)));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public UnsafeValues getUnsafe() {
|
||||
@@ -1922,17 +2060,20 @@ public final class CraftServer implements Server {
|
||||
|
||||
@Override
|
||||
public void reloadPermissions() {
|
||||
((SimplePluginManager) pluginManager).clearPermissions();
|
||||
loadCustomPermissions();
|
||||
pluginManager.clearPermissions();
|
||||
if (com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions();
|
||||
for (Plugin plugin : pluginManager.getPlugins()) {
|
||||
plugin.getDescription().getPermissions().forEach((perm) -> {
|
||||
for (Permission perm : plugin.getDescription().getPermissions()) {
|
||||
try {
|
||||
pluginManager.addPermission(perm);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
getLogger().log(Level.WARNING, "Plugin " + plugin.getDescription().getFullName() + " tried to register permission '" + perm.getName() + "' but it's already registered", ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions();
|
||||
DefaultPermissions.registerCorePermissions();
|
||||
CraftDefaultPermissions.registerCorePermissions();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,9 +5,10 @@ import com.destroystokyo.paper.profile.CraftPlayerProfile;
|
||||
import com.destroystokyo.paper.profile.PlayerProfile;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import io.akarin.api.internal.mixin.IMixinWorldServer;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -17,6 +18,7 @@ import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -28,8 +30,48 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
import net.minecraft.server.*;
|
||||
import net.minecraft.server.PacketPlayOutTitle.EnumTitleAction;
|
||||
import net.minecraft.server.AdvancementDataPlayer;
|
||||
import net.minecraft.server.AdvancementProgress;
|
||||
import net.minecraft.server.AttributeInstance;
|
||||
import net.minecraft.server.AttributeMapServer;
|
||||
import net.minecraft.server.AttributeModifiable;
|
||||
import net.minecraft.server.AttributeRanged;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.ChatComponentText;
|
||||
import net.minecraft.server.Container;
|
||||
import net.minecraft.server.Entity;
|
||||
import net.minecraft.server.EntityHuman;
|
||||
import net.minecraft.server.EntityLiving;
|
||||
import net.minecraft.server.EntityPlayer;
|
||||
import net.minecraft.server.EntityTracker;
|
||||
import net.minecraft.server.EntityTrackerEntry;
|
||||
import net.minecraft.server.EnumChatFormat;
|
||||
import net.minecraft.server.EnumGamemode;
|
||||
import net.minecraft.server.IChatBaseComponent;
|
||||
import net.minecraft.server.MapIcon;
|
||||
import net.minecraft.server.MinecraftKey;
|
||||
import net.minecraft.server.NBTTagCompound;
|
||||
import net.minecraft.server.PacketDataSerializer;
|
||||
import net.minecraft.server.PacketPlayOutBlockChange;
|
||||
import net.minecraft.server.PacketPlayOutChat;
|
||||
import net.minecraft.server.PacketPlayOutCustomPayload;
|
||||
import net.minecraft.server.PacketPlayOutCustomSoundEffect;
|
||||
import net.minecraft.server.PacketPlayOutMap;
|
||||
import net.minecraft.server.PacketPlayOutNamedSoundEffect;
|
||||
import net.minecraft.server.PacketPlayOutPlayerInfo;
|
||||
import net.minecraft.server.PacketPlayOutPlayerListHeaderFooter;
|
||||
import net.minecraft.server.PacketPlayOutSpawnPosition;
|
||||
import net.minecraft.server.PacketPlayOutStopSound;
|
||||
import net.minecraft.server.PacketPlayOutTitle;
|
||||
import net.minecraft.server.PacketPlayOutUpdateAttributes;
|
||||
import net.minecraft.server.PacketPlayOutUpdateHealth;
|
||||
import net.minecraft.server.PacketPlayOutWorldEvent;
|
||||
import net.minecraft.server.PacketPlayOutWorldParticles;
|
||||
import net.minecraft.server.PlayerConnection;
|
||||
import net.minecraft.server.TileEntitySign;
|
||||
import net.minecraft.server.Vec3D;
|
||||
import net.minecraft.server.WhiteListEntry;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
@@ -40,12 +82,14 @@ import org.bukkit.Statistic;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic.Type;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.configuration.serialization.DelegateDeserialization;
|
||||
import org.bukkit.conversations.Conversation;
|
||||
import org.bukkit.conversations.ConversationAbandonedEvent;
|
||||
import org.bukkit.conversations.ManuallyAbandonedConversationCanceller;
|
||||
import org.bukkit.craftbukkit.*;
|
||||
import org.bukkit.craftbukkit.CraftParticle;
|
||||
import org.bukkit.craftbukkit.block.CraftSign;
|
||||
import org.bukkit.craftbukkit.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.conversations.ConversationTracker;
|
||||
import org.bukkit.craftbukkit.CraftEffect;
|
||||
import org.bukkit.craftbukkit.CraftOfflinePlayer;
|
||||
@@ -78,6 +122,7 @@ import javax.annotation.Nullable;
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Make hidden players thread-safe (safety issue)
|
||||
* 2) Add locks for entry operation (safety issue)
|
||||
*/
|
||||
@DelegateDeserialization(CraftOfflinePlayer.class)
|
||||
public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
@@ -86,7 +131,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
private boolean hasPlayedBefore = false;
|
||||
private final ConversationTracker conversationTracker = new ConversationTracker();
|
||||
private final Set<String> channels = new HashSet<String>();
|
||||
private final Map<UUID, Set<WeakReference<Plugin>>> hiddenPlayers = Maps.newConcurrentMap(); // new HashMap<>(); // Akarin
|
||||
private final Map<UUID, Set<WeakReference<Plugin>>> hiddenPlayers = new java.util.concurrent.ConcurrentHashMap<>(); // Akarin
|
||||
private static final WeakHashMap<Plugin, WeakReference<Plugin>> pluginWeakReferences = new WeakHashMap<>();
|
||||
private int hash = 0;
|
||||
private double health = 20;
|
||||
@@ -126,12 +171,10 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
perm.recalculatePermissions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() {
|
||||
return server.getPlayer(getUniqueId()) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
if (getHandle().playerConnection == null) return null;
|
||||
|
||||
@@ -193,7 +236,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
@Override
|
||||
public void sendActionBar(String message) {
|
||||
if (getHandle().playerConnection == null || message == null || message.isEmpty()) return;
|
||||
getHandle().playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(message), ChatMessageType.GAME_INFO));
|
||||
getHandle().playerConnection.sendPacket(new PacketPlayOutChat(new net.minecraft.server.ChatComponentText(message), net.minecraft.server.ChatMessageType.GAME_INFO));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -204,10 +247,21 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
|
||||
@Override
|
||||
public void setPlayerListHeaderFooter(BaseComponent[] header, BaseComponent[] footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.header = header;
|
||||
packet.footer = footer;
|
||||
getHandle().playerConnection.sendPacket(packet);
|
||||
if (header != null) {
|
||||
String headerJson = net.md_5.bungee.chat.ComponentSerializer.toString(footer);
|
||||
playerListHeader = net.minecraft.server.ChatBaseComponent.ChatSerializer.jsonToComponent(headerJson);
|
||||
} else {
|
||||
playerListHeader = null;
|
||||
}
|
||||
|
||||
if (footer != null) {
|
||||
String headerJson = net.md_5.bungee.chat.ComponentSerializer.toString(footer);
|
||||
playerListFooter = net.minecraft.server.ChatBaseComponent.ChatSerializer.jsonToComponent(headerJson);
|
||||
} else {
|
||||
playerListFooter = null;
|
||||
}
|
||||
|
||||
updatePlayerListHeaderFooter();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -300,14 +354,55 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
if (name == null) {
|
||||
name = getName();
|
||||
}
|
||||
getHandle().listName = name.equals(getName()) ? null : CraftChatMessage.fromString(name)[0];
|
||||
for (EntityPlayer player : server.getHandle().players) {
|
||||
getHandle().listName = name.equals(getName()) ? null : CraftChatMessage.fromStringOrNull(name);
|
||||
for (EntityPlayer player : (List<EntityPlayer>)server.getHandle().players) {
|
||||
if (player.getBukkitEntity().canSee(this)) {
|
||||
player.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_DISPLAY_NAME, getHandle()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IChatBaseComponent playerListHeader;
|
||||
private IChatBaseComponent playerListFooter;
|
||||
|
||||
@Override
|
||||
public String getPlayerListHeader() {
|
||||
return (playerListHeader == null) ? null : CraftChatMessage.fromComponent(playerListHeader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayerListFooter() {
|
||||
return (playerListFooter == null) ? null : CraftChatMessage.fromComponent(playerListFooter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerListHeader(String header) {
|
||||
this.playerListHeader = CraftChatMessage.fromStringOrNull(header, true); // Paper - fix up spigot tab API
|
||||
updatePlayerListHeaderFooter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerListFooter(String footer) {
|
||||
this.playerListFooter = CraftChatMessage.fromStringOrNull(footer, true); // Paper - fix up spigot tab API
|
||||
updatePlayerListHeaderFooter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerListHeaderFooter(String header, String footer) {
|
||||
this.playerListHeader = CraftChatMessage.fromStringOrNull(header, true); // Paper - fix up spigot tab API
|
||||
this.playerListFooter = CraftChatMessage.fromStringOrNull(footer, true); // Paper - fix up spigot tab API
|
||||
updatePlayerListHeaderFooter();
|
||||
}
|
||||
|
||||
private void updatePlayerListHeaderFooter() {
|
||||
if (getHandle().playerConnection == null) return;
|
||||
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.header = (this.playerListHeader == null) ? new ChatComponentText("") : this.playerListHeader;
|
||||
packet.footer = (this.playerListFooter == null) ? new ChatComponentText("") : this.playerListFooter;
|
||||
getHandle().playerConnection.sendPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof OfflinePlayer)) {
|
||||
@@ -400,7 +495,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
}
|
||||
|
||||
float f = (float) Math.pow(2.0D, (note - 12.0D) / 12.0D);
|
||||
getHandle().playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(CraftSound.getSoundEffect("block.note." + instrumentName), net.minecraft.server.SoundCategory.RECORDS, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, f));
|
||||
getHandle().playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(CraftSound.getSoundEffect("block.note_block." + instrumentName), net.minecraft.server.SoundCategory.RECORDS, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, f));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -441,7 +536,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
break;
|
||||
}
|
||||
float f = (float) Math.pow(2.0D, (note.getId() - 12.0D) / 12.0D);
|
||||
getHandle().playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(CraftSound.getSoundEffect("block.note." + instrumentName), net.minecraft.server.SoundCategory.RECORDS, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, f));
|
||||
getHandle().playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(CraftSound.getSoundEffect("block.note_block." + instrumentName), net.minecraft.server.SoundCategory.RECORDS, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, f));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -466,7 +561,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
public void playSound(Location loc, String sound, org.bukkit.SoundCategory category, float volume, float pitch) {
|
||||
if (loc == null || sound == null || category == null || getHandle().playerConnection == null) return;
|
||||
|
||||
PacketPlayOutCustomSoundEffect packet = new PacketPlayOutCustomSoundEffect(sound, net.minecraft.server.SoundCategory.valueOf(category.name()), loc.getX(), loc.getY(), loc.getZ(), volume, pitch);
|
||||
PacketPlayOutCustomSoundEffect packet = new PacketPlayOutCustomSoundEffect(new MinecraftKey(sound), net.minecraft.server.SoundCategory.valueOf(category.name()), new Vec3D(loc.getX(), loc.getY(), loc.getZ()), volume, pitch);
|
||||
getHandle().playerConnection.sendPacket(packet);
|
||||
}
|
||||
|
||||
@@ -488,18 +583,17 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
@Override
|
||||
public void stopSound(String sound, org.bukkit.SoundCategory category) {
|
||||
if (getHandle().playerConnection == null) return;
|
||||
PacketDataSerializer packetdataserializer = new PacketDataSerializer(Unpooled.buffer());
|
||||
|
||||
packetdataserializer.a(category == null ? "" : net.minecraft.server.SoundCategory.valueOf(category.name()).a());
|
||||
packetdataserializer.a(sound);
|
||||
getHandle().playerConnection.sendPacket(new PacketPlayOutCustomPayload("MC|StopSound", packetdataserializer));
|
||||
getHandle().playerConnection.sendPacket(new PacketPlayOutStopSound(new MinecraftKey(sound), category == null ? net.minecraft.server.SoundCategory.MASTER : net.minecraft.server.SoundCategory.valueOf(category.name())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playEffect(Location loc, Effect effect, int data) {
|
||||
if (getHandle().playerConnection == null) return;
|
||||
|
||||
spigot().playEffect(loc, effect, data, 0, 0, 0, 0, 1, 1, 64); // Spigot
|
||||
int packetData = effect.getId();
|
||||
PacketPlayOutWorldEvent packet = new PacketPlayOutWorldEvent(packetData, new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()), data, false);
|
||||
getHandle().playerConnection.sendPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -516,16 +610,21 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
|
||||
@Override
|
||||
public void sendBlockChange(Location loc, Material material, byte data) {
|
||||
sendBlockChange(loc, material.getId(), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBlockChange(Location loc, int material, byte data) {
|
||||
if (getHandle().playerConnection == null) return;
|
||||
|
||||
PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(((CraftWorld) loc.getWorld()).getHandle(), new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
|
||||
|
||||
packet.block = CraftMagicNumbers.getBlock(material).fromLegacyData(data);
|
||||
packet.block = CraftMagicNumbers.getBlock(material, data);
|
||||
getHandle().playerConnection.sendPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBlockChange(Location loc, BlockData block) {
|
||||
if (getHandle().playerConnection == null) return;
|
||||
|
||||
PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(((CraftWorld) loc.getWorld()).getHandle(), new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
|
||||
|
||||
packet.block = ((CraftBlockData) block).getState();
|
||||
getHandle().playerConnection.sendPacket(packet);
|
||||
}
|
||||
|
||||
@@ -594,7 +693,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
Collection<MapIcon> icons = new ArrayList<MapIcon>();
|
||||
for (MapCursor cursor : data.cursors) {
|
||||
if (cursor.isVisible()) {
|
||||
icons.add(new MapIcon(MapIcon.Type.a(cursor.getRawType()), cursor.getX(), cursor.getY(), cursor.getDirection()));
|
||||
icons.add(new MapIcon(MapIcon.Type.a(cursor.getRawType()), cursor.getX(), cursor.getY(), cursor.getDirection(), CraftChatMessage.fromStringOrNull(cursor.getCaption())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,6 +703,10 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
|
||||
@Override
|
||||
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
|
||||
Preconditions.checkArgument(location != null, "location");
|
||||
Preconditions.checkArgument(location.getWorld() != null, "location.world");
|
||||
location.checkFinite();
|
||||
|
||||
EntityPlayer entity = getHandle();
|
||||
|
||||
if (getHealth() == 0 || entity.dead) {
|
||||
@@ -908,9 +1011,9 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
@Override
|
||||
public void setWhitelisted(boolean value) {
|
||||
if (value) {
|
||||
server.getHandle().addWhitelist(getProfile());
|
||||
server.getHandle().getWhitelist().add(new WhiteListEntry(getProfile()));
|
||||
} else {
|
||||
server.getHandle().removeWhitelist(getProfile());
|
||||
server.getHandle().getWhitelist().remove(getProfile());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -935,10 +1038,10 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
public int applyMending(int amount) {
|
||||
EntityPlayer handle = getHandle();
|
||||
// Logic copied from EntityExperienceOrb and remapped to unobfuscated methods/properties
|
||||
ItemStack itemstack = EnchantmentManager.getRandomEquippedItemWithEnchant(Enchantments.MENDING, handle);
|
||||
if (!itemstack.isEmpty() && itemstack.hasDamage()) {
|
||||
net.minecraft.server.ItemStack itemstack = net.minecraft.server.EnchantmentManager.getRandomEquippedItemWithEnchant(net.minecraft.server.Enchantments.MENDING, handle);
|
||||
if (!itemstack.isEmpty() && itemstack.getItem().usesDurability()) {
|
||||
|
||||
EntityExperienceOrb orb = new EntityExperienceOrb(handle.world);
|
||||
net.minecraft.server.EntityExperienceOrb orb = new net.minecraft.server.EntityExperienceOrb(handle.world);
|
||||
orb.value = amount;
|
||||
orb.spawnReason = org.bukkit.entity.ExperienceOrb.SpawnReason.CUSTOM;
|
||||
orb.locX = handle.locX;
|
||||
@@ -1107,10 +1210,12 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
EntityTracker tracker = ((WorldServer) entity.world).tracker;
|
||||
// Paper end
|
||||
|
||||
synchronized (((IMixinWorldServer) entity.world).trackLock()) { // Akarin
|
||||
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
|
||||
if (entry != null) {
|
||||
entry.clear(getHandle());
|
||||
}
|
||||
} // Akarin
|
||||
|
||||
// Remove the hidden player from this player user list, if they're on it
|
||||
if (other.sentListPacket) {
|
||||
@@ -1157,12 +1262,12 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
|
||||
getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other));
|
||||
|
||||
synchronized (((IMixinWorldServer) entity.world).trackLock()) { // Akarin
|
||||
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
|
||||
if (entry != null && !entry.trackedPlayers.contains(getHandle())) {
|
||||
tracker.entriesLock.lock(); // Akarin
|
||||
entry.updatePlayer(getHandle());
|
||||
tracker.entriesLock.unlock(); // Akarin
|
||||
}
|
||||
} // Akarin
|
||||
}
|
||||
// Paper start
|
||||
private void reregisterPlayer(EntityPlayer player) {
|
||||
@@ -1171,7 +1276,6 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
registerPlayer(player);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void setPlayerProfile(PlayerProfile profile) {
|
||||
EntityPlayer self = getHandle();
|
||||
self.setProfile(CraftPlayerProfile.asAuthlibCopy(profile));
|
||||
@@ -1181,7 +1285,6 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
}
|
||||
refreshPlayer();
|
||||
}
|
||||
@Override
|
||||
public PlayerProfile getPlayerProfile() {
|
||||
return new CraftPlayerProfile(this).clone();
|
||||
}
|
||||
@@ -1195,10 +1298,10 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
reregisterPlayer(handle);
|
||||
|
||||
//Respawn the player then update their position and selected slot
|
||||
connection.sendPacket(new PacketPlayOutRespawn(handle.dimension, handle.world.getDifficulty(), handle.world.getWorldData().getType(), handle.playerInteractManager.getGameMode()));
|
||||
connection.sendPacket(new net.minecraft.server.PacketPlayOutRespawn(handle.dimension, handle.world.getDifficulty(), handle.world.getWorldData().getType(), handle.playerInteractManager.getGameMode()));
|
||||
handle.updateAbilities();
|
||||
connection.sendPacket(new PacketPlayOutPosition(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch(), new HashSet<>(), 0));
|
||||
MinecraftServer.getServer().getPlayerList().updateClient(handle);
|
||||
connection.sendPacket(new net.minecraft.server.PacketPlayOutPosition(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch(), new HashSet<>(), 0));
|
||||
net.minecraft.server.MinecraftServer.getServer().getPlayerList().updateClient(handle);
|
||||
|
||||
if (this.isOp()) {
|
||||
this.setOp(false);
|
||||
@@ -1340,7 +1443,8 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
if (getHandle().playerConnection == null) return;
|
||||
|
||||
if (channels.contains(channel)) {
|
||||
PacketPlayOutCustomPayload packet = new PacketPlayOutCustomPayload(channel, new PacketDataSerializer(Unpooled.wrappedBuffer(message)));
|
||||
channel = StandardMessenger.validateAndCorrectChannel(channel);
|
||||
PacketPlayOutCustomPayload packet = new PacketPlayOutCustomPayload(new MinecraftKey(channel), new PacketDataSerializer(Unpooled.wrappedBuffer(message)));
|
||||
getHandle().playerConnection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
@@ -1367,13 +1471,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
}
|
||||
|
||||
public void addChannel(String channel) {
|
||||
com.google.common.base.Preconditions.checkState( DISABLE_CHANNEL_LIMIT || channels.size() < 128, "Too many channels registered" ); // Spigot // Paper - flag to disable channel limit
|
||||
com.google.common.base.Preconditions.checkState( DISABLE_CHANNEL_LIMIT || channels.size() < 128, "Too many channels registered" ); // Spigot // Paper - flag to disable channel limit
|
||||
channel = StandardMessenger.validateAndCorrectChannel(channel);
|
||||
if (channels.add(channel)) {
|
||||
server.getPluginManager().callEvent(new PlayerRegisterChannelEvent(this, channel));
|
||||
}
|
||||
}
|
||||
|
||||
public void removeChannel(String channel) {
|
||||
channel = StandardMessenger.validateAndCorrectChannel(channel);
|
||||
if (channels.remove(channel)) {
|
||||
server.getPluginManager().callEvent(new PlayerUnregisterChannelEvent(this, channel));
|
||||
}
|
||||
@@ -1400,7 +1506,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
}
|
||||
}
|
||||
|
||||
getHandle().playerConnection.sendPacket(new PacketPlayOutCustomPayload("REGISTER", new PacketDataSerializer(Unpooled.wrappedBuffer(stream.toByteArray()))));
|
||||
getHandle().playerConnection.sendPacket(new PacketPlayOutCustomPayload(new MinecraftKey("register"), new PacketDataSerializer(Unpooled.wrappedBuffer(stream.toByteArray()))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1437,7 +1543,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
}
|
||||
// Paper start
|
||||
if (prop == Property.REPAIR_COST && container instanceof net.minecraft.server.ContainerAnvil) {
|
||||
((ContainerAnvil) container).levelCost = value;
|
||||
((net.minecraft.server.ContainerAnvil) container).levelCost = value;
|
||||
}
|
||||
// Paper end
|
||||
getHandle().setContainerData(container, prop.getId(), value);
|
||||
@@ -1508,7 +1614,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
|
||||
@Override
|
||||
public float getFlySpeed() {
|
||||
return getHandle().abilities.flySpeed * 2f;
|
||||
return (float) getHandle().abilities.flySpeed * 2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1610,13 +1716,21 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(getHandle().getId(), set));
|
||||
sendHealthUpdate();
|
||||
}
|
||||
getHandle().getDataWatcher().set(EntityLiving.HEALTH, getScaledHealth());
|
||||
getHandle().getDataWatcher().set(EntityLiving.HEALTH, (float) getScaledHealth());
|
||||
|
||||
getHandle().maxHealthCache = getMaxHealth();
|
||||
}
|
||||
|
||||
public void sendHealthUpdate() {
|
||||
getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel()));
|
||||
// Paper start - cancellable death event
|
||||
//getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel()));
|
||||
PacketPlayOutUpdateHealth packet = new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel());
|
||||
if (this.getHandle().queueHealthUpdatePacket) {
|
||||
this.getHandle().queuedHealthUpdatePacket = packet;
|
||||
} else {
|
||||
this.getHandle().playerConnection.sendPacket(packet);
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
|
||||
public void injectScaledMaxHealth(Collection<AttributeInstance> collection, boolean force) {
|
||||
@@ -1663,19 +1777,19 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
getHandle().playerConnection.sendPacket(times);
|
||||
|
||||
if (title != null) {
|
||||
PacketPlayOutTitle packetTitle = new PacketPlayOutTitle(EnumTitleAction.TITLE, CraftChatMessage.fromString(title)[0]);
|
||||
PacketPlayOutTitle packetTitle = new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TITLE, CraftChatMessage.fromStringOrNull(title));
|
||||
getHandle().playerConnection.sendPacket(packetTitle);
|
||||
}
|
||||
|
||||
if (subtitle != null) {
|
||||
PacketPlayOutTitle packetSubtitle = new PacketPlayOutTitle(EnumTitleAction.SUBTITLE, CraftChatMessage.fromString(subtitle)[0]);
|
||||
PacketPlayOutTitle packetSubtitle = new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.SUBTITLE, CraftChatMessage.fromStringOrNull(subtitle));
|
||||
getHandle().playerConnection.sendPacket(packetSubtitle);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetTitle() {
|
||||
PacketPlayOutTitle packetReset = new PacketPlayOutTitle(EnumTitleAction.RESET, null);
|
||||
PacketPlayOutTitle packetReset = new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.RESET, null);
|
||||
getHandle().playerConnection.sendPacket(packetReset);
|
||||
}
|
||||
|
||||
@@ -1739,7 +1853,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
if (data != null && !particle.getDataType().isInstance(data)) {
|
||||
throw new IllegalArgumentException("data should be " + particle.getDataType() + " got " + data.getClass());
|
||||
}
|
||||
PacketPlayOutWorldParticles packetplayoutworldparticles = new PacketPlayOutWorldParticles(CraftParticle.toNMS(particle), true, (float) x, (float) y, (float) z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count, CraftParticle.toData(particle, data));
|
||||
PacketPlayOutWorldParticles packetplayoutworldparticles = new PacketPlayOutWorldParticles(CraftParticle.toNMS(particle, data), true, (float) x, (float) y, (float) z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count);
|
||||
getHandle().playerConnection.sendPacket(packetplayoutworldparticles);
|
||||
|
||||
}
|
||||
@@ -1763,7 +1877,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
// Paper end
|
||||
}
|
||||
|
||||
@Override
|
||||
// Paper start
|
||||
public void setAffectsSpawning(boolean affects) {
|
||||
this.getHandle().affectsSpawning = affects;
|
||||
}
|
||||
@@ -1782,6 +1896,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
public void setViewDistance(int viewDistance) {
|
||||
((WorldServer) getHandle().world).getPlayerChunkMap().updateViewDistance(getHandle(), viewDistance);
|
||||
}
|
||||
// Paper end
|
||||
|
||||
@Override
|
||||
public void updateCommands() {
|
||||
if (getHandle().playerConnection == null) return;
|
||||
|
||||
getHandle().server.getCommandDispatcher().a(getHandle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourcePack(String url, String hash) {
|
||||
@@ -1809,6 +1931,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
this.resourcePackStatus = status;
|
||||
}
|
||||
|
||||
//Paper start
|
||||
public float getCooldownPeriod() {
|
||||
return getHandle().getCooldownPeriod();
|
||||
}
|
||||
|
||||
public float getCooledAttackStrength(float adjustTicks) {
|
||||
return getHandle().getCooledAttackStrength(adjustTicks);
|
||||
}
|
||||
|
||||
public void resetCooldown() {
|
||||
getHandle().resetCooldown();
|
||||
}
|
||||
//Paper end
|
||||
|
||||
// Spigot start
|
||||
private final Player.Spigot spigot = new Player.Spigot()
|
||||
{
|
||||
@@ -1834,64 +1970,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
{
|
||||
if ( getHealth() <= 0 && isOnline() )
|
||||
{
|
||||
server.getServer().getPlayerList().moveToWorld( getHandle(), 0, false );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playEffect( Location location, Effect effect, int id, int data, float offsetX, float offsetY, float offsetZ, float speed, int particleCount, int radius )
|
||||
{
|
||||
Validate.notNull( location, "Location cannot be null" );
|
||||
Validate.notNull( effect, "Effect cannot be null" );
|
||||
Validate.notNull( location.getWorld(), "World cannot be null" );
|
||||
Packet packet;
|
||||
if ( effect.getType() != Effect.Type.PARTICLE )
|
||||
{
|
||||
int packetData = effect.getId();
|
||||
packet = new PacketPlayOutWorldEvent( packetData, new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ() ), id, false );
|
||||
} else
|
||||
{
|
||||
net.minecraft.server.EnumParticle particle = null;
|
||||
int[] extra = null;
|
||||
for ( net.minecraft.server.EnumParticle p : net.minecraft.server.EnumParticle.values() )
|
||||
{
|
||||
if ( effect.getName().startsWith( p.b().replace("_", "") ) )
|
||||
{
|
||||
particle = p;
|
||||
if ( effect.getData() != null )
|
||||
{
|
||||
if ( effect.getData().equals( org.bukkit.Material.class ) )
|
||||
{
|
||||
extra = new int[]{ id };
|
||||
} else
|
||||
{
|
||||
extra = new int[]{ (data << 12) | (id & 0xFFF) };
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( extra == null )
|
||||
{
|
||||
extra = new int[0];
|
||||
}
|
||||
packet = new PacketPlayOutWorldParticles( particle, true, (float) location.getX(), (float) location.getY(), (float) location.getZ(), offsetX, offsetY, offsetZ, speed, particleCount, extra );
|
||||
}
|
||||
int distance;
|
||||
radius *= radius;
|
||||
if ( getHandle().playerConnection == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ( !location.getWorld().equals( getWorld() ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
distance = (int) getLocation().distanceSquared( location );
|
||||
if ( distance <= radius )
|
||||
{
|
||||
getHandle().playerConnection.sendPacket( packet );
|
||||
server.getServer().getPlayerList().moveToWorld( getHandle(), net.minecraft.server.DimensionManager.OVERWORLD, false );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1922,7 +2001,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
public void sendMessage(BaseComponent... components) {
|
||||
if ( getHandle().playerConnection == null ) return;
|
||||
|
||||
PacketPlayOutChat packet = new PacketPlayOutChat(null, ChatMessageType.CHAT);
|
||||
PacketPlayOutChat packet = new PacketPlayOutChat(null, net.minecraft.server.ChatMessageType.CHAT);
|
||||
packet.components = components;
|
||||
getHandle().playerConnection.sendPacket(packet);
|
||||
}
|
||||
@@ -1936,7 +2015,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
public void sendMessage(net.md_5.bungee.api.ChatMessageType position, BaseComponent... components) {
|
||||
if ( getHandle().playerConnection == null ) return;
|
||||
|
||||
PacketPlayOutChat packet = new PacketPlayOutChat(null, ChatMessageType.a((byte) position.ordinal()));
|
||||
PacketPlayOutChat packet = new PacketPlayOutChat(null, net.minecraft.server.ChatMessageType.a((byte) position.ordinal()));
|
||||
// Action bar doesn't render colours, replace colours with legacy section symbols
|
||||
if (position == net.md_5.bungee.api.ChatMessageType.ACTION_BAR) {
|
||||
components = new BaseComponent[]{new net.md_5.bungee.api.chat.TextComponent(BaseComponent.toLegacyText(components))};
|
||||
@@ -1945,14 +2024,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
getHandle().playerConnection.sendPacket(packet);
|
||||
}
|
||||
|
||||
// Paper start
|
||||
@Override
|
||||
public int getPing()
|
||||
{
|
||||
return getHandle().ping;
|
||||
}
|
||||
// Paper end
|
||||
};
|
||||
|
||||
@Override
|
||||
public Player.Spigot spigot()
|
||||
{
|
||||
return spigot;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user