Block precise TechHider (preparation for block HullHider)
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Untested
Dieser Commit ist enthalten in:
Ursprung
2d7bff131e
Commit
a69a5bfe36
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,6 +1,3 @@
|
|||||||
# Package Files
|
|
||||||
*.jar
|
|
||||||
|
|
||||||
# Gradle
|
# Gradle
|
||||||
.gradle
|
.gradle
|
||||||
**/build/
|
**/build/
|
||||||
|
@ -20,122 +20,23 @@
|
|||||||
package de.steamwar.techhider;
|
package de.steamwar.techhider;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.util.Collections;
|
||||||
import java.nio.LongBuffer;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class ChunkHider14 extends ChunkHider9 {
|
public class ChunkHider14 extends ChunkHider9 {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected byte[] dataHider(int obfuscationTarget, Set<Integer> obfuscate, byte[] data, Integer primaryBitMask) {
|
protected void dataHider(PosEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, ByteBuf in, ByteBuf out) {
|
||||||
ByteBuf buffer = UnpooledByteBufAllocator.DEFAULT.directBuffer(data.length + 100);
|
out.writeShort(in.readShort()); // Block count
|
||||||
int i = 0;
|
byte bitsPerBlock = in.readByte();
|
||||||
|
out.writeByte(bitsPerBlock);
|
||||||
while(primaryBitMask != 0){
|
|
||||||
while((primaryBitMask & 1) == 0){
|
|
||||||
primaryBitMask >>= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.writeBytes(data, i, 2); // Block count
|
|
||||||
i += 2;
|
|
||||||
byte bitsPerBlock = data[i++];
|
|
||||||
buffer.writeByte(bitsPerBlock);
|
|
||||||
|
|
||||||
if(bitsPerBlock < 9) {
|
if(bitsPerBlock < 9) {
|
||||||
int paletteLength = ProtocolUtils.readVarInt(data, i);
|
obfuscationTarget = ChunkHider.processPalette(obfuscationTarget, obfuscate, in, out);
|
||||||
int paletteLengthLength = ProtocolUtils.readVarIntLength(data, i);
|
obfuscate = Collections.emptySet();
|
||||||
buffer.writeBytes(data, i, paletteLengthLength);
|
|
||||||
i += paletteLengthLength;
|
|
||||||
for(int actPaletteId = 0; actPaletteId < paletteLength; actPaletteId++){
|
|
||||||
int blockId = ProtocolUtils.readVarInt(data, i);
|
|
||||||
int actPaletteLength = ProtocolUtils.readVarIntLength(data, i);
|
|
||||||
|
|
||||||
if(obfuscate.contains(blockId)){
|
|
||||||
buffer.writeBytes(ProtocolUtils.writeVarInt(obfuscationTarget));
|
|
||||||
}else{
|
|
||||||
buffer.writeBytes(data, i, actPaletteLength);
|
|
||||||
}
|
|
||||||
i += actPaletteLength;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//We modify only the chunk palette for performance reasons
|
processDataArray(locationEvaluator, obfuscationTarget, obfuscate, in, out, bitsPerBlock);
|
||||||
int dataArrayLength = ProtocolUtils.readVarInt(data, i);
|
|
||||||
int dataArrayLengthLength = ProtocolUtils.readVarIntLength(data, i);
|
|
||||||
buffer.writeBytes(data, i, dataArrayLength * 8 + dataArrayLengthLength);
|
|
||||||
i += dataArrayLengthLength;
|
|
||||||
i += dataArrayLength * 8;
|
|
||||||
}else{
|
|
||||||
//Full Chunk/no palette, so the chunk has to be crawled through
|
|
||||||
int dataArrayLength = ProtocolUtils.readVarInt(data, i);
|
|
||||||
int dataArrayLengthLength = ProtocolUtils.readVarIntLength(data, i);
|
|
||||||
buffer.writeBytes(data, i, dataArrayLengthLength);
|
|
||||||
i += dataArrayLengthLength;
|
|
||||||
|
|
||||||
ByteBuffer source = ByteBuffer.wrap(data, i, dataArrayLength * 8);
|
|
||||||
VariableValueArray values = new VariableValueArray(bitsPerBlock, dataArrayLength, source.asLongBuffer());
|
|
||||||
|
|
||||||
for(int pos = 0; pos < 4096; pos++){
|
|
||||||
if(obfuscate.contains(values.get(pos))){
|
|
||||||
values.set(pos, obfuscationTarget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(long l : values.backing)
|
|
||||||
buffer.writeLong(l);
|
|
||||||
|
|
||||||
i += dataArrayLength * 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
primaryBitMask >>= 1;
|
|
||||||
}
|
|
||||||
buffer.writeBytes(data, i, data.length - i); // MC appends a 0 byte at the end if there is a full chunk, idk why
|
|
||||||
|
|
||||||
data = new byte[buffer.readableBytes()];
|
|
||||||
buffer.readBytes(data);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class VariableValueArray {
|
|
||||||
private final long[] backing;
|
|
||||||
private final int bitsPerValue;
|
|
||||||
private final long valueMask;
|
|
||||||
|
|
||||||
public VariableValueArray(int bitsPerEntry, int dataArrayLength, LongBuffer buffer) {
|
|
||||||
this.bitsPerValue = bitsPerEntry;
|
|
||||||
this.backing = new long[dataArrayLength];
|
|
||||||
buffer.get(backing);
|
|
||||||
this.valueMask = (1L << this.bitsPerValue) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int get(int index) {
|
|
||||||
index *= bitsPerValue;
|
|
||||||
int i0 = index >> 6;
|
|
||||||
int i1 = index & 0x3f;
|
|
||||||
|
|
||||||
long value = backing[i0] >>> i1;
|
|
||||||
|
|
||||||
// The value is divided over two long values
|
|
||||||
if (i1 + bitsPerValue > 64) {
|
|
||||||
value |= backing[++i0] << 64 - i1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int) (value & valueMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(int index, int value) {
|
|
||||||
index *= bitsPerValue;
|
|
||||||
int i0 = index >> 6;
|
|
||||||
int i1 = index & 0x3f;
|
|
||||||
|
|
||||||
backing[i0] = this.backing[i0] & ~(this.valueMask << i1) | (value & valueMask) << i1;
|
|
||||||
int i2 = i1 + bitsPerValue;
|
|
||||||
// The value is divided over two long values
|
|
||||||
if (i2 > 64) {
|
|
||||||
i0++;
|
|
||||||
backing[i0] = backing[i0] & -(1L << i2 - 64) | value >> 64 - i1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,21 +30,23 @@ import java.util.function.UnaryOperator;
|
|||||||
public class ProtocolWrapper14 extends ProtocolWrapper8 {
|
public class ProtocolWrapper14 extends ProtocolWrapper8 {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.BypassEvaluator bypass) {
|
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator) {
|
||||||
UnaryOperator<Object> blockBreakCloner = ProtocolUtils.shallowCloneGenerator(blockBreakPacket);
|
UnaryOperator<Object> blockBreakCloner = ProtocolUtils.shallowCloneGenerator(blockBreakPacket);
|
||||||
Reflection.FieldAccessor<?> blockBreakPosition = Reflection.getField(blockBreakPacket, TechHider.blockPosition, 0);
|
Reflection.FieldAccessor<?> blockBreakPosition = Reflection.getField(blockBreakPacket, TechHider.blockPosition, 0);
|
||||||
Reflection.FieldAccessor<?> blockBreakBlockData = Reflection.getField(blockBreakPacket, TechHider.iBlockData, 0);
|
Reflection.FieldAccessor<?> blockBreakBlockData = Reflection.getField(blockBreakPacket, TechHider.iBlockData, 0);
|
||||||
|
|
||||||
return (p, packet) -> {
|
return (p, packet) -> {
|
||||||
Object pos = blockBreakPosition.get(packet);
|
switch (locationEvaluator.checkBlockPos(p, blockBreakPosition.get(packet))) {
|
||||||
if(bypass.bypass(p, ProtocolUtils.posToChunk(TechHider.blockPositionX.get(pos)), ProtocolUtils.posToChunk(TechHider.blockPositionZ.get(pos))))
|
case SKIP:
|
||||||
return packet;
|
return packet;
|
||||||
|
case CHECK:
|
||||||
if(TechHider.iBlockDataHidden(obfuscate, blockBreakBlockData.get(packet))){
|
if(!TechHider.iBlockDataHidden(obfuscate, blockBreakBlockData.get(packet)))
|
||||||
|
return packet;
|
||||||
|
default:
|
||||||
packet = blockBreakCloner.apply(packet);
|
packet = blockBreakCloner.apply(packet);
|
||||||
blockBreakBlockData.set(packet, obfuscationTarget);
|
blockBreakBlockData.set(packet, obfuscationTarget);
|
||||||
}
|
|
||||||
return packet;
|
return packet;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,21 +22,19 @@ package de.steamwar.techhider;
|
|||||||
import com.comphenix.tinyprotocol.Reflection;
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
import de.steamwar.core.Core;
|
import de.steamwar.core.Core;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
import io.netty.buffer.Unpooled;
|
||||||
import net.minecraft.core.IRegistry;
|
import net.minecraft.core.IRegistry;
|
||||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
|
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
|
||||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||||
import net.minecraft.resources.MinecraftKey;
|
import net.minecraft.resources.MinecraftKey;
|
||||||
import net.minecraft.util.SimpleBitStorage;
|
import net.minecraft.util.SimpleBitStorage;
|
||||||
import net.minecraft.world.level.block.entity.TileEntityTypes;
|
import net.minecraft.world.level.block.entity.TileEntityTypes;
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.IntFunction;
|
|
||||||
import java.util.function.UnaryOperator;
|
import java.util.function.UnaryOperator;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -49,29 +47,41 @@ public class ChunkHider18 implements ChunkHider {
|
|||||||
private static final UnaryOperator<Object> chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class);
|
private static final UnaryOperator<Object> chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class);
|
||||||
private static final UnaryOperator<Object> chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class);
|
private static final UnaryOperator<Object> chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class);
|
||||||
|
|
||||||
private static final Reflection.FieldAccessor<Integer> chunkX = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 0);
|
private static final Reflection.FieldAccessor<Integer> chunkXField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 0);
|
||||||
private static final Reflection.FieldAccessor<Integer> chunkZ = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 1);
|
private static final Reflection.FieldAccessor<Integer> chunkZField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 1);
|
||||||
private static final Reflection.FieldAccessor<ClientboundLevelChunkPacketData> chunkData = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0);
|
private static final Reflection.FieldAccessor<ClientboundLevelChunkPacketData> chunkData = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0);
|
||||||
|
|
||||||
private static final Reflection.FieldAccessor<byte[]> dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0);
|
private static final Reflection.FieldAccessor<byte[]> dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0);
|
||||||
private static final Reflection.FieldAccessor<List> tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0);
|
private static final Reflection.FieldAccessor<List> tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider.BypassEvaluator bypass, int obfuscationTarget, Set<Integer> obfuscate, Set<String> hiddenBlockEntities) {
|
public BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider.LocationEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, Set<String> hiddenBlockEntities) {
|
||||||
return (p, packet) -> {
|
return (p, packet) -> {
|
||||||
if(bypass.bypass(p, chunkX.get(packet), chunkZ.get(packet)))
|
int chunkX = chunkXField.get(packet);
|
||||||
|
int chunkZ = chunkZField.get(packet);
|
||||||
|
if (locationEvaluator.skipChunk(p, chunkX, chunkZ))
|
||||||
return packet;
|
return packet;
|
||||||
|
|
||||||
packet = chunkPacketCloner.apply(packet);
|
packet = chunkPacketCloner.apply(packet);
|
||||||
Object data = chunkDataCloner.apply(chunkData.get(packet));
|
Object dataWrapper = chunkDataCloner.apply(chunkData.get(packet));
|
||||||
|
|
||||||
tileEntities.set(data, ((List<?>)tileEntities.get(data)).stream().filter(te -> tileEntityVisible(hiddenBlockEntities, te)).collect(Collectors.toList()));
|
tileEntities.set(dataWrapper, ((List<?>)tileEntities.get(dataWrapper)).stream().filter(te -> tileEntityVisible(hiddenBlockEntities, te)).collect(Collectors.toList()));
|
||||||
|
|
||||||
World world = p.getWorld();
|
ByteBuf in = Unpooled.wrappedBuffer(dataField.get(dataWrapper));
|
||||||
int sections = (world.getMaxHeight() - world.getMinHeight()) / 16;
|
ByteBuf out = Unpooled.buffer(in.readableBytes() + 64);
|
||||||
dataField.set(data, dataHider(obfuscationTarget, obfuscate, dataField.get(data), sections));
|
int xOffset = 16*chunkX;
|
||||||
|
int zOffset = 16*chunkZ;
|
||||||
|
for(int yOffset = p.getWorld().getMinHeight(); yOffset < p.getWorld().getMaxHeight(); yOffset += 16) {
|
||||||
|
int finalYOffset = yOffset;
|
||||||
|
dataHider((x, y, z) -> locationEvaluator.check(p, x+xOffset, y+finalYOffset, z+zOffset), obfuscationTarget, obfuscate, in, out);
|
||||||
|
}
|
||||||
|
out.writeBytes(in); // MC appends a 0 byte at the end if there is a full chunk, idk why
|
||||||
|
|
||||||
chunkData.set(packet, data);
|
byte[] data = new byte[out.readableBytes()];
|
||||||
|
out.readBytes(data);
|
||||||
|
dataField.set(dataWrapper, data);
|
||||||
|
|
||||||
|
chunkData.set(packet, dataWrapper);
|
||||||
return packet;
|
return packet;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -85,72 +95,62 @@ public class ChunkHider18 implements ChunkHider {
|
|||||||
return !hiddenBlockEntities.contains((String) getName.invoke(getKey.invoke(tileEntityTypes, entityType.get(tile))));
|
return !hiddenBlockEntities.contains((String) getName.invoke(getKey.invoke(tileEntityTypes, entityType.get(tile))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] dataHider(int obfuscationTarget, Set<Integer> obfuscate, byte[] data, int sections) {
|
private void dataHider(PosEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, ByteBuf in, ByteBuf out) {
|
||||||
ByteBuf buffer = UnpooledByteBufAllocator.DEFAULT.directBuffer(data.length + 100);
|
out.writeShort(in.readShort()); // Block count
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
while(sections-- > 0) {
|
containerWalker(in, out, 15, obfuscationTarget, obfuscate, (paletteTarget, dataArrayLength, bitsPerBlock) -> {
|
||||||
buffer.writeBytes(data, i, 2); // Block count
|
Set<Integer> palettedObfuscate;
|
||||||
i += 2;
|
|
||||||
|
|
||||||
i = containerWalker(data, buffer, i, 15, blockId -> obfuscate.contains(blockId) ? obfuscationTarget : blockId, (curI, dataArrayLength, bitsPerBlock) -> {
|
|
||||||
if(bitsPerBlock < 15) {
|
if(bitsPerBlock < 15) {
|
||||||
buffer.writeBytes(data, curI, dataArrayLength * 8);
|
palettedObfuscate = Collections.emptySet();
|
||||||
} else {
|
} else {
|
||||||
ByteBuffer source = ByteBuffer.wrap(data, curI, dataArrayLength * 8);
|
paletteTarget = obfuscationTarget;
|
||||||
|
palettedObfuscate = obfuscate;
|
||||||
|
}
|
||||||
|
|
||||||
long[] array = new long[dataArrayLength];
|
long[] array = new long[dataArrayLength];
|
||||||
source.asLongBuffer().get(array);
|
for(int i = 0; i < dataArrayLength; i++)
|
||||||
|
array[i] = in.readLong();
|
||||||
SimpleBitStorage values = new SimpleBitStorage(bitsPerBlock, 4096, array);
|
SimpleBitStorage values = new SimpleBitStorage(bitsPerBlock, 4096, array);
|
||||||
|
|
||||||
for (int pos = 0; pos < 4096; pos++) {
|
for (int y = 0; y < 16; y++) {
|
||||||
if (obfuscate.contains(values.a(pos))) {
|
for (int z = 0; z < 16; z++) {
|
||||||
values.b(pos, obfuscationTarget);
|
for (int x = 0; x < 16; x++) {
|
||||||
|
int pos = (((y * 16) + z) * 16) + x;
|
||||||
|
|
||||||
|
switch (locationEvaluator.test(x, y, z)) {
|
||||||
|
case SKIP:
|
||||||
|
break;
|
||||||
|
case CHECK:
|
||||||
|
if(!palettedObfuscate.contains(values.a(pos)))
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
values.b(pos, paletteTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (long l : values.a())
|
for (long l : values.a())
|
||||||
buffer.writeLong(l);
|
out.writeLong(l);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
i = containerWalker(data, buffer, i, 6, value -> value, (curI, dataArrayLength, bitsPerBlock) -> buffer.writeBytes(data, curI, dataArrayLength * 8));
|
containerWalker(in, out, 6, 0, Collections.emptySet(), (paletteTarget, dataArrayLength, bitsPerBlock) -> out.writeBytes(in, dataArrayLength * 8)); // Biomes
|
||||||
}
|
|
||||||
buffer.writeBytes(data, i, data.length - i); // MC appends a 0 byte at the end if there is a full chunk, idk why
|
|
||||||
|
|
||||||
byte[] outdata = new byte[buffer.readableBytes()];
|
|
||||||
buffer.readBytes(outdata);
|
|
||||||
return outdata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int containerWalker(byte[] data, ByteBuf buffer, int i, int globalPalette, IntFunction<Integer> palette, TriConsumer<Integer, Integer, Byte> dataArray) {
|
private void containerWalker(ByteBuf in, ByteBuf out, int globalPalette, int obfuscationTarget, Set<Integer> obfuscate, TriConsumer<Integer, Integer, Byte> dataArray) {
|
||||||
byte bitsPerBlock = data[i++];
|
byte bitsPerBlock = in.readByte();
|
||||||
buffer.writeByte(bitsPerBlock);
|
out.writeByte(bitsPerBlock);
|
||||||
|
|
||||||
|
//blockId -> obfuscate.contains(blockId) ? obfuscationTarget : blockId
|
||||||
if(bitsPerBlock == 0) {
|
if(bitsPerBlock == 0) {
|
||||||
int paletteValue = ProtocolUtils.readVarInt(data, i);
|
int value = ProtocolUtils.readVarInt(in);
|
||||||
i += ProtocolUtils.readVarIntLength(data, i);
|
ProtocolUtils.writeVarInt(out, obfuscate.contains(value) ? obfuscationTarget : value);
|
||||||
buffer.writeBytes(ProtocolUtils.writeVarInt(palette.apply(paletteValue)));
|
|
||||||
}else if(bitsPerBlock < globalPalette) {
|
}else if(bitsPerBlock < globalPalette) {
|
||||||
int paletteLength = ProtocolUtils.readVarInt(data, i);
|
obfuscationTarget = ChunkHider.processPalette(obfuscationTarget, obfuscate, in, out);
|
||||||
int paletteLengthLength = ProtocolUtils.readVarIntLength(data, i);
|
|
||||||
buffer.writeBytes(data, i, paletteLengthLength);
|
|
||||||
i += paletteLengthLength;
|
|
||||||
|
|
||||||
for(int actPaletteId = 0; actPaletteId < paletteLength; actPaletteId++) {
|
|
||||||
int paletteValue = ProtocolUtils.readVarInt(data, i);
|
|
||||||
i += ProtocolUtils.readVarIntLength(data, i);
|
|
||||||
buffer.writeBytes(ProtocolUtils.writeVarInt(palette.apply(paletteValue)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int dataArrayLength = ProtocolUtils.readVarInt(data, i);
|
int dataArrayLength = ProtocolUtils.readVarInt(in);
|
||||||
int dataArrayLengthLength = ProtocolUtils.readVarIntLength(data, i);
|
ProtocolUtils.writeVarInt(out, dataArrayLength);
|
||||||
buffer.writeBytes(data, i, dataArrayLengthLength);
|
dataArray.accept(obfuscationTarget, dataArrayLength, bitsPerBlock);
|
||||||
i += dataArrayLengthLength;
|
|
||||||
|
|
||||||
dataArray.accept(i, dataArrayLength, bitsPerBlock);
|
|
||||||
i += dataArrayLength * 8;
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface TriConsumer<X, Y, Z> {
|
private interface TriConsumer<X, Y, Z> {
|
||||||
|
@ -28,24 +28,56 @@ import net.minecraft.world.level.block.state.IBlockData;
|
|||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.UnaryOperator;
|
|
||||||
|
|
||||||
public class ProtocolWrapper18 implements ProtocolWrapper {
|
public class ProtocolWrapper18 implements ProtocolWrapper {
|
||||||
|
|
||||||
private static final Reflection.FieldAccessor<SectionPosition> multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPosition.class, 0);
|
private static final Reflection.FieldAccessor<SectionPosition> multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPosition.class, 0);
|
||||||
|
private static final Reflection.FieldAccessor<short[]> multiBlockChangePos = Reflection.getField(TechHider.multiBlockChangePacket, short[].class, 0);
|
||||||
private static final Reflection.FieldAccessor<IBlockData[]> multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, IBlockData[].class, 0);
|
private static final Reflection.FieldAccessor<IBlockData[]> multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, IBlockData[].class, 0);
|
||||||
private static final BiFunction<Object, UnaryOperator<Object>, Object> iBlockDataArrayCloner = ProtocolUtils.arrayCloneGenerator(TechHider.iBlockData);
|
|
||||||
@Override
|
@Override
|
||||||
public BiFunction<Player, Object, Object> multiBlockChangeGenerator(Object obfuscationTarget, Set<Material> obfuscate, TechHider.BypassEvaluator bypass) {
|
public BiFunction<Player, Object, Object> multiBlockChangeGenerator(Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator) {
|
||||||
return (p, packet) -> {
|
return (p, packet) -> {
|
||||||
Object chunkCoords = multiBlockChangeChunk.get(packet);
|
Object chunkCoords = multiBlockChangeChunk.get(packet);
|
||||||
if(bypass.bypass(p, TechHider.blockPositionX.get(chunkCoords), TechHider.blockPositionZ.get(chunkCoords)))
|
int chunkX = TechHider.blockPositionX.get(chunkCoords);
|
||||||
|
int chunkY = TechHider.blockPositionY.get(chunkCoords);
|
||||||
|
int chunkZ = TechHider.blockPositionZ.get(chunkCoords);
|
||||||
|
if(locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ))
|
||||||
return packet;
|
return packet;
|
||||||
|
|
||||||
packet = TechHider.multiBlockChangeCloner.apply(packet);
|
packet = TechHider.multiBlockChangeCloner.apply(packet);
|
||||||
multiBlockChangeBlocks.set(packet, iBlockDataArrayCloner.apply(multiBlockChangeBlocks.get(packet), block -> TechHider.iBlockDataHidden(obfuscate, block) ? obfuscationTarget : block));
|
final short[] oldPos = multiBlockChangePos.get(packet);
|
||||||
|
final IBlockData[] oldBlocks = multiBlockChangeBlocks.get(packet);
|
||||||
|
ArrayList<Short> poss = new ArrayList<>(oldPos.length);
|
||||||
|
ArrayList<IBlockData> blocks = new ArrayList<>(oldPos.length);
|
||||||
|
for(int i = 0; i < oldPos.length; i++) {
|
||||||
|
short pos = oldPos[i];
|
||||||
|
IBlockData block = oldBlocks[i];
|
||||||
|
switch(locationEvaluator.check(p, 16*chunkX + (pos >> 8 & 0xF), 16*chunkY + (pos & 0xF), 16*chunkZ + (pos >> 4 & 0xF))) {
|
||||||
|
case SKIP:
|
||||||
|
poss.add(pos);
|
||||||
|
blocks.add(block);
|
||||||
|
break;
|
||||||
|
case CHECK:
|
||||||
|
poss.add(pos);
|
||||||
|
blocks.add(TechHider.iBlockDataHidden(obfuscate, block) ? (IBlockData) obfuscationTarget : block);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(blocks.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
short[] newPos = new short[poss.size()];
|
||||||
|
for(int i = 0; i < newPos.length; i++)
|
||||||
|
newPos[i] = poss.get(i);
|
||||||
|
|
||||||
|
multiBlockChangePos.set(packet, newPos);
|
||||||
|
multiBlockChangeBlocks.set(packet, blocks.toArray());
|
||||||
return packet;
|
return packet;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -58,16 +90,18 @@ public class ProtocolWrapper18 implements ProtocolWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.BypassEvaluator bypass) {
|
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator) {
|
||||||
return (p, packet) -> {
|
return (p, packet) -> {
|
||||||
PacketPlayOutBlockBreak breakPacket = (PacketPlayOutBlockBreak) packet;
|
PacketPlayOutBlockBreak breakPacket = (PacketPlayOutBlockBreak) packet;
|
||||||
if(bypass.bypass(p, ProtocolUtils.posToChunk(TechHider.blockPositionX.get(breakPacket.b())), ProtocolUtils.posToChunk(TechHider.blockPositionZ.get(breakPacket.b()))))
|
switch (locationEvaluator.checkBlockPos(p, breakPacket.b())) {
|
||||||
|
case SKIP:
|
||||||
return packet;
|
return packet;
|
||||||
|
case CHECK:
|
||||||
if(!TechHider.iBlockDataHidden(obfuscate, breakPacket.c()))
|
if(!TechHider.iBlockDataHidden(obfuscate, breakPacket.c()))
|
||||||
return packet;
|
return packet;
|
||||||
|
default:
|
||||||
return new PacketPlayOutBlockBreak(breakPacket.b(), (IBlockData) obfuscationTarget, breakPacket.d(), breakPacket.a());
|
return new PacketPlayOutBlockBreak(breakPacket.b(), (IBlockData) obfuscationTarget, breakPacket.d(), breakPacket.a());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ import java.util.function.BiFunction;
|
|||||||
public class ProtocolWrapper19 extends ProtocolWrapper18 {
|
public class ProtocolWrapper19 extends ProtocolWrapper18 {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.BypassEvaluator bypass) {
|
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ public class ChunkHider8 implements ChunkHider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider.BypassEvaluator bypass, int obfuscationTarget, Set<Integer> obfuscate, Set<String> hiddenBlockEntities) {
|
public BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider.LocationEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, Set<String> hiddenBlockEntities) {
|
||||||
return (player, packet) -> packet;
|
return (player, packet) -> packet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,9 @@ import com.comphenix.tinyprotocol.Reflection;
|
|||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.UnaryOperator;
|
|
||||||
|
|
||||||
public class ProtocolWrapper8 implements ProtocolWrapper {
|
public class ProtocolWrapper8 implements ProtocolWrapper {
|
||||||
private static final Class<?> chunkCoordinateIntPair = Reflection.getClass("{nms}.ChunkCoordIntPair");
|
private static final Class<?> chunkCoordinateIntPair = Reflection.getClass("{nms}.ChunkCoordIntPair");
|
||||||
@ -34,25 +34,41 @@ public class ProtocolWrapper8 implements ProtocolWrapper {
|
|||||||
private static final Reflection.FieldAccessor<Integer> chunkCoordinateZ = Reflection.getField(chunkCoordinateIntPair, int.class, 1);
|
private static final Reflection.FieldAccessor<Integer> chunkCoordinateZ = Reflection.getField(chunkCoordinateIntPair, int.class, 1);
|
||||||
private static final Class<?> multiBlockChangeInfo = Reflection.getClass("{nms}.PacketPlayOutMultiBlockChange$MultiBlockChangeInfo");
|
private static final Class<?> multiBlockChangeInfo = Reflection.getClass("{nms}.PacketPlayOutMultiBlockChange$MultiBlockChangeInfo");
|
||||||
private static final Reflection.ConstructorInvoker multiBlockChangeInfoConstructor = Reflection.getConstructor(multiBlockChangeInfo, TechHider.multiBlockChangePacket, short.class, TechHider.iBlockData);
|
private static final Reflection.ConstructorInvoker multiBlockChangeInfoConstructor = Reflection.getConstructor(multiBlockChangeInfo, TechHider.multiBlockChangePacket, short.class, TechHider.iBlockData);
|
||||||
private static final BiFunction<Object, UnaryOperator<Object>, Object> multiBlockChangeInfoArrayCloner = ProtocolUtils.arrayCloneGenerator(multiBlockChangeInfo);
|
|
||||||
private static final Reflection.FieldAccessor<?> multiBlockChangeInfoBlock = Reflection.getField(multiBlockChangeInfo, TechHider.iBlockData, 0);
|
private static final Reflection.FieldAccessor<?> multiBlockChangeInfoBlock = Reflection.getField(multiBlockChangeInfo, TechHider.iBlockData, 0);
|
||||||
private static final Reflection.FieldAccessor<?> multiBlockChangeInfoPos = Reflection.getField(multiBlockChangeInfo, short.class, 0);
|
private static final Reflection.FieldAccessor<?> multiBlockChangeInfoPos = Reflection.getField(multiBlockChangeInfo, short.class, 0);
|
||||||
private static final Class<?> multiBlockChangeInfoArray = Reflection.getClass("[L{nms}.PacketPlayOutMultiBlockChange$MultiBlockChangeInfo;");
|
private static final Class<?> multiBlockChangeInfoArray = Reflection.getClass("[L{nms}.PacketPlayOutMultiBlockChange$MultiBlockChangeInfo;");
|
||||||
private static final Reflection.FieldAccessor<?> multiBlockChangeInfos = Reflection.getField(TechHider.multiBlockChangePacket, multiBlockChangeInfoArray, 0);
|
private static final Reflection.FieldAccessor<?> multiBlockChangeInfos = Reflection.getField(TechHider.multiBlockChangePacket, multiBlockChangeInfoArray, 0);
|
||||||
@Override
|
@Override
|
||||||
public BiFunction<Player, Object, Object> multiBlockChangeGenerator(Object obfuscationTarget, Set<Material> obfuscate, TechHider.BypassEvaluator bypass) {
|
public BiFunction<Player, Object, Object> multiBlockChangeGenerator(Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator) {
|
||||||
return (p, packet) -> {
|
return (p, packet) -> {
|
||||||
Object chunkCoords = multiBlockChangeChunk.get(packet);
|
Object chunkCoords = multiBlockChangeChunk.get(packet);
|
||||||
if(bypass.bypass(p, chunkCoordinateX.get(chunkCoords), chunkCoordinateZ.get(chunkCoords)))
|
int chunkX = chunkCoordinateX.get(chunkCoords);
|
||||||
|
int chunkZ = chunkCoordinateZ.get(chunkCoords);
|
||||||
|
if(locationEvaluator.skipChunk(p, chunkX, chunkZ))
|
||||||
return packet;
|
return packet;
|
||||||
|
|
||||||
Object modpacket = TechHider.multiBlockChangeCloner.apply(packet);
|
packet = TechHider.multiBlockChangeCloner.apply(packet);
|
||||||
multiBlockChangeInfos.set(modpacket, multiBlockChangeInfoArrayCloner.apply(multiBlockChangeInfos.get(modpacket), mbci -> {
|
Object[] mbcis = (Object[]) multiBlockChangeInfos.get(packet);
|
||||||
if(TechHider.iBlockDataHidden(obfuscate, multiBlockChangeInfoBlock.get(mbci)))
|
ArrayList<Object> blockChangeInfos = new ArrayList<>(mbcis.length);
|
||||||
return multiBlockChangeInfoConstructor.invoke(modpacket, multiBlockChangeInfoPos.get(mbci), obfuscationTarget);
|
for(Object mbci : mbcis) {
|
||||||
return mbci;
|
short pos = (short) multiBlockChangeInfoPos.get(mbci);
|
||||||
}));
|
switch(locationEvaluator.check(p, 16*chunkX + (pos >> 12 & 0xF), pos & 0xFF, 16*chunkZ + (pos >> 8 & 0xF))) {
|
||||||
return modpacket;
|
case SKIP:
|
||||||
|
blockChangeInfos.add(mbci);
|
||||||
|
break;
|
||||||
|
case CHECK:
|
||||||
|
blockChangeInfos.add(TechHider.iBlockDataHidden(obfuscate, multiBlockChangeInfoBlock.get(mbci)) ? multiBlockChangeInfoConstructor.invoke(packet, pos, obfuscationTarget) : mbci);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(blockChangeInfos.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
multiBlockChangeInfos.set(packet, blockChangeInfos.toArray(mbcis));
|
||||||
|
return packet;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +79,7 @@ public class ProtocolWrapper8 implements ProtocolWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.BypassEvaluator bypass) {
|
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator bypass) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,15 +21,14 @@ package de.steamwar.techhider;
|
|||||||
|
|
||||||
import com.comphenix.tinyprotocol.Reflection;
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
import io.netty.buffer.Unpooled;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.UnaryOperator;
|
import java.util.function.UnaryOperator;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class ChunkHider9 extends ChunkHider8 {
|
public class ChunkHider9 extends ChunkHider8 {
|
||||||
@ -46,9 +45,11 @@ public class ChunkHider9 extends ChunkHider8 {
|
|||||||
private static final Reflection.MethodInvoker nbtTagGetString = Reflection.getTypedMethod(nbtTagCompound, null, String.class, String.class);
|
private static final Reflection.MethodInvoker nbtTagGetString = Reflection.getTypedMethod(nbtTagCompound, null, String.class, String.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider.BypassEvaluator bypass, int obfuscationTarget, Set<Integer> obfuscate, Set<String> hiddenBlockEntities) {
|
public BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider.LocationEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, Set<String> hiddenBlockEntities) {
|
||||||
return (p, packet) -> {
|
return (p, packet) -> {
|
||||||
if(bypass.bypass(p, mapChunkX.get(packet), mapChunkZ.get(packet)))
|
int chunkX = mapChunkX.get(packet);
|
||||||
|
int chunkZ = mapChunkZ.get(packet);
|
||||||
|
if (locationEvaluator.skipChunk(p, chunkX, chunkZ))
|
||||||
return packet;
|
return packet;
|
||||||
|
|
||||||
packet = mapChunkCloner.apply(packet);
|
packet = mapChunkCloner.apply(packet);
|
||||||
@ -56,53 +57,108 @@ public class ChunkHider9 extends ChunkHider8 {
|
|||||||
nbttag -> !hiddenBlockEntities.contains((String) nbtTagGetString.invoke(nbttag, "id"))
|
nbttag -> !hiddenBlockEntities.contains((String) nbtTagGetString.invoke(nbttag, "id"))
|
||||||
).collect(Collectors.toList()));
|
).collect(Collectors.toList()));
|
||||||
|
|
||||||
byte[] data = dataHider(obfuscationTarget, obfuscate, mapChunkData.get(packet), mapChunkBitMask.get(packet));
|
int xOffset = 16*chunkX;
|
||||||
|
int zOffset = 16*chunkZ;
|
||||||
|
int primaryBitMask = mapChunkBitMask.get(packet);
|
||||||
|
ByteBuf in = Unpooled.wrappedBuffer(mapChunkData.get(packet));
|
||||||
|
ByteBuf out = Unpooled.buffer(in.readableBytes() + 64);
|
||||||
|
for(int chunkY = 0; chunkY < p.getWorld().getMaxHeight(); chunkY++) {
|
||||||
|
if(((1 << chunkY) & primaryBitMask) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int yOffset = 16*chunkY;
|
||||||
|
dataHider((x, y, z) -> locationEvaluator.check(p, x+xOffset, y+yOffset, z+zOffset), obfuscationTarget, obfuscate, in, out);
|
||||||
|
}
|
||||||
|
byte[] data = new byte[out.readableBytes()];
|
||||||
|
out.readBytes(data);
|
||||||
mapChunkData.set(packet, data);
|
mapChunkData.set(packet, data);
|
||||||
|
|
||||||
return packet;
|
return packet;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected byte[] dataHider(int obfuscationTarget, Set<Integer> obfuscate, byte[] data, Integer primaryBitMask) {
|
protected void dataHider(PosEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, ByteBuf in, ByteBuf out) {
|
||||||
ByteBuf buffer = UnpooledByteBufAllocator.DEFAULT.directBuffer(data.length + 100);
|
byte bitsPerBlock = in.readByte();
|
||||||
int i = 0;
|
out.writeByte(bitsPerBlock);
|
||||||
|
|
||||||
while(i < data.length){
|
int paletteTarget = ChunkHider.processPalette(obfuscationTarget, obfuscate, in, out);
|
||||||
byte bitsPerBlock = data[i++];
|
if(bitsPerBlock < 13) {
|
||||||
buffer.writeByte(bitsPerBlock);
|
obfuscationTarget = paletteTarget;
|
||||||
|
obfuscate = Collections.emptySet();
|
||||||
if(bitsPerBlock != 13){
|
|
||||||
int paletteLength = ProtocolUtils.readVarInt(data, i);
|
|
||||||
int paletteLengthLength = ProtocolUtils.readVarIntLength(data, i);
|
|
||||||
buffer.writeBytes(data, i, paletteLengthLength);
|
|
||||||
i += paletteLengthLength;
|
|
||||||
|
|
||||||
for(int actPaletteId = 0; actPaletteId < paletteLength; actPaletteId++){
|
|
||||||
int entry = ProtocolUtils.readVarInt(data, i);
|
|
||||||
i += ProtocolUtils.readVarIntLength(data, i);
|
|
||||||
|
|
||||||
if(obfuscate.contains(entry)){
|
|
||||||
entry = obfuscationTarget;
|
|
||||||
}
|
|
||||||
buffer.writeBytes(ProtocolUtils.writeVarInt(entry));
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
buffer.writeByte(data[++i]); //Empty palette
|
|
||||||
Bukkit.getLogger().log(Level.SEVERE, "Full chunk occured");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int dataArrayLength = ProtocolUtils.readVarInt(data, i);
|
processDataArray(locationEvaluator, obfuscationTarget, obfuscate, in, out, bitsPerBlock);
|
||||||
int dataArrayLengthLength = ProtocolUtils.readVarIntLength(data, i);
|
|
||||||
buffer.writeBytes(data, i, dataArrayLength*8 + dataArrayLengthLength);
|
|
||||||
i += dataArrayLengthLength;
|
|
||||||
i += dataArrayLength * 8;
|
|
||||||
|
|
||||||
buffer.writeBytes(data, i, 4096);
|
out.writeBytes(in, 4096); //Skylight (Not in Nether/End!!!) 2048 + Blocklight 2048
|
||||||
i += 4096; //Skylight (Not in Nether/End!!!) 2048 + Blocklight 2048
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data = new byte[buffer.readableBytes()];
|
protected void processDataArray(PosEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, ByteBuf in, ByteBuf out, int bitsPerBlock) {
|
||||||
buffer.readBytes(data);
|
int dataArrayLength = ProtocolUtils.readVarInt(in);
|
||||||
return data;
|
ProtocolUtils.writeVarInt(out, dataArrayLength);
|
||||||
|
|
||||||
|
VariableValueArray values = new VariableValueArray(bitsPerBlock, dataArrayLength, in);
|
||||||
|
for (int y = 0; y < 16; y++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
int pos = (((y * 16) + z) * 16) + x;
|
||||||
|
|
||||||
|
switch (locationEvaluator.test(x, y, z)) {
|
||||||
|
case SKIP:
|
||||||
|
break;
|
||||||
|
case CHECK:
|
||||||
|
if(!obfuscate.contains(values.get(pos)))
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
values.set(pos, obfuscationTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(long l : values.backing)
|
||||||
|
out.writeLong(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class VariableValueArray {
|
||||||
|
private final long[] backing;
|
||||||
|
private final int bitsPerValue;
|
||||||
|
private final long valueMask;
|
||||||
|
|
||||||
|
public VariableValueArray(int bitsPerEntry, int dataArrayLength, ByteBuf in) {
|
||||||
|
this.bitsPerValue = bitsPerEntry;
|
||||||
|
this.valueMask = (1L << this.bitsPerValue) - 1;
|
||||||
|
|
||||||
|
this.backing = new long[dataArrayLength];
|
||||||
|
for(int i = 0; i < dataArrayLength; i++)
|
||||||
|
backing[i] = in.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int get(int index) {
|
||||||
|
index *= bitsPerValue;
|
||||||
|
int i0 = index >> 6;
|
||||||
|
int i1 = index & 0x3f;
|
||||||
|
|
||||||
|
long value = backing[i0] >>> i1;
|
||||||
|
|
||||||
|
// The value is divided over two long values
|
||||||
|
if (i1 + bitsPerValue > 64)
|
||||||
|
value |= backing[++i0] << 64 - i1;
|
||||||
|
|
||||||
|
return (int) (value & valueMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int index, int value) {
|
||||||
|
index *= bitsPerValue;
|
||||||
|
int i0 = index >> 6;
|
||||||
|
int i1 = index & 0x3f;
|
||||||
|
|
||||||
|
backing[i0] = backing[i0] & ~(valueMask << i1) | (value & valueMask) << i1;
|
||||||
|
int i2 = i1 + bitsPerValue;
|
||||||
|
// The value is divided over two long values
|
||||||
|
if (i2 > 64) {
|
||||||
|
i0++;
|
||||||
|
backing[i0] = backing[i0] & -(1L << i2 - 64) | value >> 64 - i1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ package de.steamwar.techhider;
|
|||||||
|
|
||||||
import de.steamwar.core.Core;
|
import de.steamwar.core.Core;
|
||||||
import de.steamwar.core.VersionDependent;
|
import de.steamwar.core.VersionDependent;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -30,5 +31,29 @@ public interface ChunkHider {
|
|||||||
ChunkHider impl = VersionDependent.getVersionImpl(Core.getInstance());
|
ChunkHider impl = VersionDependent.getVersionImpl(Core.getInstance());
|
||||||
|
|
||||||
Class<?> mapChunkPacket();
|
Class<?> mapChunkPacket();
|
||||||
BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider.BypassEvaluator bypass, int obfuscationTarget, Set<Integer> obfuscate, Set<String> hiddenBlockEntities);
|
BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider.LocationEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, Set<String> hiddenBlockEntities);
|
||||||
|
|
||||||
|
static int processPalette(int obfuscationTarget, Set<Integer> obfuscate, ByteBuf in, ByteBuf out) {
|
||||||
|
int paletteLength = ProtocolUtils.readVarInt(in);
|
||||||
|
ProtocolUtils.writeVarInt(out, paletteLength);
|
||||||
|
|
||||||
|
int paletteTarget = 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < paletteLength; i++) {
|
||||||
|
int entry = ProtocolUtils.readVarInt(in);
|
||||||
|
if(obfuscate.contains(entry))
|
||||||
|
entry = obfuscationTarget;
|
||||||
|
|
||||||
|
if(entry == obfuscationTarget)
|
||||||
|
paletteTarget = i;
|
||||||
|
|
||||||
|
ProtocolUtils.writeVarInt(out, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return paletteTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PosEvaluator {
|
||||||
|
TechHider.State test(int x, int y, int z);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ package de.steamwar.techhider;
|
|||||||
import com.comphenix.tinyprotocol.Reflection;
|
import com.comphenix.tinyprotocol.Reflection;
|
||||||
import com.comphenix.tinyprotocol.TinyProtocol;
|
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
@ -40,6 +41,7 @@ public class ProtocolUtils {
|
|||||||
Bukkit.getOnlinePlayers().forEach(player -> TinyProtocol.instance.sendPacket(player, packet));
|
Bukkit.getOnlinePlayers().forEach(player -> TinyProtocol.instance.sendPacket(player, packet));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static BiFunction<Object, UnaryOperator<Object>, Object> arrayCloneGenerator(Class<?> elementClass) {
|
public static BiFunction<Object, UnaryOperator<Object>, Object> arrayCloneGenerator(Class<?> elementClass) {
|
||||||
return (array, worker) -> {
|
return (array, worker) -> {
|
||||||
int length = Array.getLength(array);
|
int length = Array.getLength(array);
|
||||||
@ -98,6 +100,7 @@ public class ProtocolUtils {
|
|||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static int readVarInt(byte[] array, int startPos) {
|
public static int readVarInt(byte[] array, int startPos) {
|
||||||
int numRead = 0;
|
int numRead = 0;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
@ -116,6 +119,35 @@ public class ProtocolUtils {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int readVarInt(ByteBuf buf) {
|
||||||
|
int numRead = 0;
|
||||||
|
int result = 0;
|
||||||
|
byte read;
|
||||||
|
do {
|
||||||
|
read = buf.readByte();
|
||||||
|
int value = (read & 0b01111111);
|
||||||
|
result |= (value << (7 * numRead));
|
||||||
|
|
||||||
|
if (++numRead > 5)
|
||||||
|
throw new SecurityException("VarInt too long");
|
||||||
|
} while ((read & 0b10000000) != 0);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeVarInt(ByteBuf buf, int value) {
|
||||||
|
do {
|
||||||
|
int temp = value & 0b01111111;
|
||||||
|
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
|
||||||
|
value >>>= 7;
|
||||||
|
if (value != 0) {
|
||||||
|
temp |= 0b10000000;
|
||||||
|
}
|
||||||
|
buf.writeByte(temp);
|
||||||
|
} while (value != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static int readVarIntLength(byte[] array, int startPos) {
|
public static int readVarIntLength(byte[] array, int startPos) {
|
||||||
int numRead = 0;
|
int numRead = 0;
|
||||||
byte read;
|
byte read;
|
||||||
@ -130,6 +162,7 @@ public class ProtocolUtils {
|
|||||||
return numRead;
|
return numRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static byte[] writeVarInt(int value) {
|
public static byte[] writeVarInt(int value) {
|
||||||
List<Byte> buffer = new ArrayList<>(5);
|
List<Byte> buffer = new ArrayList<>(5);
|
||||||
do {
|
do {
|
||||||
@ -144,6 +177,7 @@ public class ProtocolUtils {
|
|||||||
return Bytes.toArray(buffer);
|
return Bytes.toArray(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static class ChunkPos{
|
public static class ChunkPos{
|
||||||
final int x;
|
final int x;
|
||||||
final int z;
|
final int z;
|
||||||
|
@ -33,7 +33,7 @@ public interface ProtocolWrapper {
|
|||||||
|
|
||||||
boolean unfilteredTileEntityDataAction(Object packet);
|
boolean unfilteredTileEntityDataAction(Object packet);
|
||||||
|
|
||||||
BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.BypassEvaluator bypass);
|
BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator);
|
||||||
|
|
||||||
BiFunction<Player, Object, Object> multiBlockChangeGenerator(Object obfuscationTarget, Set<Material> obfuscate, TechHider.BypassEvaluator bypass);
|
BiFunction<Player, Object, Object> multiBlockChangeGenerator(Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator);
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ public class TechHider {
|
|||||||
public static final Class<?> blockPosition = Reflection.getClass("{nms.core}.BlockPosition");
|
public static final Class<?> blockPosition = Reflection.getClass("{nms.core}.BlockPosition");
|
||||||
private static final Class<?> baseBlockPosition = Reflection.getClass("{nms.core}.BaseBlockPosition");
|
private static final Class<?> baseBlockPosition = Reflection.getClass("{nms.core}.BaseBlockPosition");
|
||||||
public static final Reflection.FieldAccessor<Integer> blockPositionX = Reflection.getField(baseBlockPosition, int.class, 0);
|
public static final Reflection.FieldAccessor<Integer> blockPositionX = Reflection.getField(baseBlockPosition, int.class, 0);
|
||||||
|
public static final Reflection.FieldAccessor<Integer> blockPositionY = Reflection.getField(baseBlockPosition, int.class, 1);
|
||||||
public static final Reflection.FieldAccessor<Integer> blockPositionZ = Reflection.getField(baseBlockPosition, int.class, 2);
|
public static final Reflection.FieldAccessor<Integer> blockPositionZ = Reflection.getField(baseBlockPosition, int.class, 2);
|
||||||
|
|
||||||
public static final Class<?> iBlockData = Reflection.getClass("{nms.world.level.block.state}.IBlockData");
|
public static final Class<?> iBlockData = Reflection.getClass("{nms.world.level.block.state}.IBlockData");
|
||||||
@ -53,24 +54,29 @@ public class TechHider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final Map<Class<?>, BiFunction<Player, Object, Object>> techhiders = new HashMap<>();
|
private final Map<Class<?>, BiFunction<Player, Object, Object>> techhiders = new HashMap<>();
|
||||||
private final BypassEvaluator bypass;
|
private final LocationEvaluator locationEvaluator;
|
||||||
private final Object obfuscationTarget;
|
private final Object obfuscationTarget;
|
||||||
private final Set<Material> obfuscate;
|
private final Set<Material> obfuscate;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public TechHider(BypassEvaluator bypass, Material obfuscationTarget, Set<Material> obfuscate, Set<String> hiddenBlockEntities) {
|
public TechHider(BypassEvaluator bypass, Material obfuscationTarget, Set<Material> obfuscate, Set<String> hiddenBlockEntities) {
|
||||||
this.bypass = bypass;
|
this((LocationEvaluator) (player, x, z) -> bypass.bypass(player, ProtocolUtils.posToChunk(x), ProtocolUtils.posToChunk(z)), obfuscationTarget, obfuscate, hiddenBlockEntities);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TechHider(LocationEvaluator locationEvaluator, Material obfuscationTarget, Set<Material> obfuscate, Set<String> hiddenBlockEntities) {
|
||||||
|
this.locationEvaluator = locationEvaluator;
|
||||||
this.obfuscate = obfuscate;
|
this.obfuscate = obfuscate;
|
||||||
this.obfuscationTarget = getBlockDataByBlock.invoke(getBlockByMaterial.invoke(null, obfuscationTarget));
|
this.obfuscationTarget = getBlockDataByBlock.invoke(getBlockByMaterial.invoke(null, obfuscationTarget));
|
||||||
|
|
||||||
techhiders.put(blockActionPacket, this::blockActionHider);
|
techhiders.put(blockActionPacket, this::blockActionHider);
|
||||||
techhiders.put(blockChangePacket, this::blockChangeHider);
|
techhiders.put(blockChangePacket, this::blockChangeHider);
|
||||||
techhiders.put(tileEntityDataPacket, this::tileEntityDataHider);
|
techhiders.put(tileEntityDataPacket, this::tileEntityDataHider);
|
||||||
techhiders.put(multiBlockChangePacket,ProtocolWrapper.impl.multiBlockChangeGenerator(this.obfuscationTarget, obfuscate, bypass));
|
techhiders.put(multiBlockChangePacket, ProtocolWrapper.impl.multiBlockChangeGenerator(this.obfuscationTarget, obfuscate, locationEvaluator));
|
||||||
techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(bypass, BlockIds.impl.materialToId(obfuscationTarget), obfuscate.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet()), hiddenBlockEntities));
|
techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(locationEvaluator, BlockIds.impl.materialToId(obfuscationTarget), obfuscate.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet()), hiddenBlockEntities));
|
||||||
|
|
||||||
if(Core.getVersion() > 12 && Core.getVersion() < 19) {
|
if(Core.getVersion() > 12 && Core.getVersion() < 19) {
|
||||||
Class<?> blockBreakClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutBlockBreak");
|
Class<?> blockBreakClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutBlockBreak");
|
||||||
techhiders.put(blockBreakClass, ProtocolWrapper.impl.blockBreakHiderGenerator(blockBreakClass, this.obfuscationTarget, obfuscate, bypass));
|
techhiders.put(blockBreakClass, ProtocolWrapper.impl.blockBreakHiderGenerator(blockBreakClass, this.obfuscationTarget, obfuscate, locationEvaluator));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Core.getVersion() > 8){
|
if(Core.getVersion() > 8){
|
||||||
@ -96,22 +102,23 @@ public class TechHider {
|
|||||||
private static final Reflection.FieldAccessor<?> blockChangePosition = Reflection.getField(blockChangePacket, blockPosition, 0);
|
private static final Reflection.FieldAccessor<?> blockChangePosition = Reflection.getField(blockChangePacket, blockPosition, 0);
|
||||||
private static final Reflection.FieldAccessor<?> blockChangeBlockData = Reflection.getField(blockChangePacket, iBlockData, 0);
|
private static final Reflection.FieldAccessor<?> blockChangeBlockData = Reflection.getField(blockChangePacket, iBlockData, 0);
|
||||||
private Object blockChangeHider(Player p, Object packet) {
|
private Object blockChangeHider(Player p, Object packet) {
|
||||||
Object pos = blockChangePosition.get(packet);
|
switch (locationEvaluator.checkBlockPos(p, blockChangePosition.get(packet))) {
|
||||||
if(bypass.bypass(p, ProtocolUtils.posToChunk(blockPositionX.get(pos)), ProtocolUtils.posToChunk(blockPositionZ.get(pos))))
|
case SKIP:
|
||||||
return packet;
|
return packet;
|
||||||
|
case CHECK:
|
||||||
if(iBlockDataHidden(obfuscate, blockChangeBlockData.get(packet))) {
|
if(!iBlockDataHidden(obfuscate, blockChangeBlockData.get(packet)))
|
||||||
|
return packet;
|
||||||
|
default:
|
||||||
packet = blockChangeCloner.apply(packet);
|
packet = blockChangeCloner.apply(packet);
|
||||||
blockChangeBlockData.set(packet, obfuscationTarget);
|
blockChangeBlockData.set(packet, obfuscationTarget);
|
||||||
}
|
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final Class<?> blockActionPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutBlockAction");
|
private static final Class<?> blockActionPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutBlockAction");
|
||||||
private static final Reflection.FieldAccessor<?> blockActionPosition = Reflection.getField(blockActionPacket, blockPosition, 0);
|
private static final Reflection.FieldAccessor<?> blockActionPosition = Reflection.getField(blockActionPacket, blockPosition, 0);
|
||||||
private Object blockActionHider(Player p, Object packet) {
|
private Object blockActionHider(Player p, Object packet) {
|
||||||
Object pos = blockActionPosition.get(packet);
|
if (locationEvaluator.checkBlockPos(p, blockActionPosition.get(packet)) == State.SKIP)
|
||||||
if(bypass.bypass(p, ProtocolUtils.posToChunk(blockPositionX.get(pos)), ProtocolUtils.posToChunk(blockPositionZ.get(pos))))
|
|
||||||
return packet;
|
return packet;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -119,16 +126,39 @@ public class TechHider {
|
|||||||
public static final Class<?> tileEntityDataPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutTileEntityData");
|
public static final Class<?> tileEntityDataPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutTileEntityData");
|
||||||
private static final Reflection.FieldAccessor<?> tileEntityDataPosition = Reflection.getField(tileEntityDataPacket, blockPosition, 0);
|
private static final Reflection.FieldAccessor<?> tileEntityDataPosition = Reflection.getField(tileEntityDataPacket, blockPosition, 0);
|
||||||
private Object tileEntityDataHider(Player p, Object packet) {
|
private Object tileEntityDataHider(Player p, Object packet) {
|
||||||
Object pos = tileEntityDataPosition.get(packet);
|
switch (locationEvaluator.checkBlockPos(p, tileEntityDataPosition.get(packet))) {
|
||||||
if(bypass.bypass(p, ProtocolUtils.posToChunk(blockPositionX.get(pos)), ProtocolUtils.posToChunk(blockPositionZ.get(pos))))
|
case SKIP:
|
||||||
return packet;
|
return packet;
|
||||||
|
case CHECK:
|
||||||
if(ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet))
|
if(ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet))
|
||||||
return packet;
|
return packet;
|
||||||
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public interface BypassEvaluator {
|
public interface BypassEvaluator {
|
||||||
boolean bypass(Player p, int chunkX, int chunkZ);
|
boolean bypass(Player p, int chunkX, int chunkZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum State {
|
||||||
|
SKIP,
|
||||||
|
CHECK,
|
||||||
|
HIDE
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface LocationEvaluator {
|
||||||
|
boolean skipChunk(Player player, int x, int z);
|
||||||
|
default boolean skipChunkSection(Player player, int x, int y, int z) {
|
||||||
|
return skipChunk(player, x, z);
|
||||||
|
}
|
||||||
|
default State check(Player player, int x, int y, int z) {
|
||||||
|
return skipChunkSection(player, ProtocolUtils.posToChunk(x), ProtocolUtils.posToChunk(y), ProtocolUtils.posToChunk(z)) ? State.SKIP : State.CHECK;
|
||||||
|
}
|
||||||
|
|
||||||
|
default State checkBlockPos(Player player, Object pos) {
|
||||||
|
return check(player, blockPositionX.get(pos), blockPositionY.get(pos), blockPositionZ.get(pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
10
build.gradle
10
build.gradle
@ -45,7 +45,6 @@ mainClassName = ''
|
|||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
jcenter()
|
|
||||||
|
|
||||||
maven {
|
maven {
|
||||||
url = uri("https://repo.codemc.io/repository/maven-snapshots/")
|
url = uri("https://repo.codemc.io/repository/maven-snapshots/")
|
||||||
@ -53,14 +52,23 @@ allprojects {
|
|||||||
|
|
||||||
maven {
|
maven {
|
||||||
url = uri('https://hub.spigotmc.org/nexus/content/repositories/snapshots/')
|
url = uri('https://hub.spigotmc.org/nexus/content/repositories/snapshots/')
|
||||||
|
content {
|
||||||
|
includeGroup('org.spigotmc')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
maven {
|
maven {
|
||||||
url = uri('https://libraries.minecraft.net')
|
url = uri('https://libraries.minecraft.net')
|
||||||
|
content {
|
||||||
|
includeGroup('com.mojang')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
maven {
|
maven {
|
||||||
url = uri('https://repo.viaversion.com')
|
url = uri('https://repo.viaversion.com')
|
||||||
|
content {
|
||||||
|
includeGroup('com.viaversion')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren