geforkt von Mirrors/AxiomPaperPlugin
Dieser Commit ist enthalten in:
Ursprung
8cfed96b39
Commit
a0367320c3
@ -6,8 +6,12 @@ import io.netty.buffer.ByteBuf;
|
|||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.server.level.ServerChunkCache;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
@ -17,7 +21,6 @@ import org.bukkit.plugin.java.JavaPlugin;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
public class AxiomPaper extends JavaPlugin implements Listener {
|
public class AxiomPaper extends JavaPlugin implements Listener {
|
||||||
|
|
||||||
@ -43,11 +46,26 @@ public class AxiomPaper extends JavaPlugin implements Listener {
|
|||||||
return worldGetHandle.invoke(world);
|
return worldGetHandle.invoke(world);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Reflection.Method<Level, LevelChunk> getChunk = Reflection.getTypedMethod(Level.class, LevelChunk.class, int.class, int.class);
|
||||||
|
public static LevelChunk getChunk(Level level, int cx, int cz) {
|
||||||
|
return getChunk.invoke(level, cx, cz);
|
||||||
|
}
|
||||||
|
|
||||||
private static final Reflection.Method<BlockPos, BlockPos> of = Reflection.getTypedMethod(BlockPos.class, BlockPos.class, long.class);
|
private static final Reflection.Method<BlockPos, BlockPos> of = Reflection.getTypedMethod(BlockPos.class, BlockPos.class, long.class);
|
||||||
public static BlockPos convert(long packed) {
|
public static BlockPos convert(long packed) {
|
||||||
return of.invoke(null, packed);
|
return of.invoke(null, packed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Reflection.Field<ServerPlayer, ServerGamePacketListenerImpl> serverPlayerConnection = Reflection.getField(ServerPlayer.class, ServerGamePacketListenerImpl.class);
|
||||||
|
public static ServerGamePacketListenerImpl getConnection(ServerPlayer player) {
|
||||||
|
return serverPlayerConnection.get(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Reflection.Method<ServerLevel, ServerChunkCache> getChunkSource = Reflection.getTypedMethod(ServerLevel.class, ServerChunkCache.class);
|
||||||
|
public static ServerChunkCache getChunkSource(ServerLevel level) {
|
||||||
|
return getChunkSource.invoke(level);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
instance = this;
|
instance = this;
|
||||||
@ -55,7 +73,7 @@ public class AxiomPaper extends JavaPlugin implements Listener {
|
|||||||
try {
|
try {
|
||||||
Bukkit.getPluginManager().registerEvents(new PaperFailMoveListener(), this);
|
Bukkit.getPluginManager().registerEvents(new PaperFailMoveListener(), this);
|
||||||
} catch (NoClassDefFoundError e) {
|
} catch (NoClassDefFoundError e) {
|
||||||
getLogger().log(Level.WARNING, "Axiom players may move too quickly according to the server. Use a current Paper version or increase the 'moved-too-quickly-multiplier' in spigot.yml.");
|
getLogger().log(java.util.logging.Level.WARNING, "Axiom players may move too quickly according to the server. Use a current Paper version or increase the 'moved-too-quickly-multiplier' in spigot.yml.");
|
||||||
}
|
}
|
||||||
|
|
||||||
for(OutChannel channel : OutChannel.values()) {
|
for(OutChannel channel : OutChannel.values()) {
|
||||||
@ -65,13 +83,13 @@ public class AxiomPaper extends JavaPlugin implements Listener {
|
|||||||
registerInChannel("hello", new HelloPacketListener(new AxiomPlayerManager()));
|
registerInChannel("hello", new HelloPacketListener(new AxiomPlayerManager()));
|
||||||
registerInChannel("set_gamemode", new SetGamemodePacketListener());
|
registerInChannel("set_gamemode", new SetGamemodePacketListener());
|
||||||
registerInChannel("set_fly_speed", new SetFlySpeedPacketListener());
|
registerInChannel("set_fly_speed", new SetFlySpeedPacketListener());
|
||||||
registerInChannel("set_block", new SetBlockPacketListener()); //TODO multiversion
|
registerInChannel("set_block", new SetBlockPacketListener());
|
||||||
registerInChannel("set_hotbar_slot", new SetHotbarSlotPacketListener());
|
registerInChannel("set_hotbar_slot", new SetHotbarSlotPacketListener());
|
||||||
registerInChannel("switch_active_hotbar", new SwitchActiveHotbarPacketListener());
|
registerInChannel("switch_active_hotbar", new SwitchActiveHotbarPacketListener());
|
||||||
registerInChannel("teleport", new TeleportPacketListener());
|
registerInChannel("teleport", new TeleportPacketListener());
|
||||||
registerInChannel("set_editor_views", new SetEditorViewsPacketListener());
|
registerInChannel("set_editor_views", new SetEditorViewsPacketListener());
|
||||||
registerInChannel("request_block_entity", new RequestBlockEntityPacketListener());
|
registerInChannel("request_block_entity", new RequestBlockEntityPacketListener());
|
||||||
registerInChannel("handle_big_payload", new SetBlockBufferPacketListener()); //TODO multiversion
|
registerInChannel("handle_big_payload", new SetBlockBufferPacketListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerInChannel(String channel, AxiomPacketListener handler) {
|
private void registerInChannel(String channel, AxiomPacketListener handler) {
|
||||||
|
@ -74,12 +74,16 @@ public class Reflection {
|
|||||||
R invoke(T target, Object... arguments);
|
R invoke(T target, Object... arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Method<T, Object> getMethod(Class<T> clazz, Class<?>... params) {
|
public static <T> Method<T, Void> getMethod(Class<T> clazz, Class<?>... params) {
|
||||||
return getTypedMethod(clazz, null, null, 0, params);
|
return getTypedMethod(clazz, null, Void.TYPE, 0, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Method<T, Object> getMethod(Class<T> clazz, String methodName, Class<?>... params) {
|
public static <T> Method<T, Void> getMethod(Class<T> clazz, int index, Class<?>... params) {
|
||||||
return getTypedMethod(clazz, methodName, null, 0, params);
|
return getTypedMethod(clazz, null, Void.TYPE, index, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Method<T, Void> getMethod(Class<T> clazz, String methodName, Class<?>... params) {
|
||||||
|
return getTypedMethod(clazz, methodName, Void.TYPE, 0, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T, R> Method<T, R> getTypedMethod(Class<T> clazz, Class<R> returnType, Class<?>... params) {
|
public static <T, R> Method<T, R> getTypedMethod(Class<T> clazz, Class<R> returnType, Class<?>... params) {
|
||||||
|
@ -25,7 +25,7 @@ public record CompressedBlockEntity(int originalSize, byte compressionDict, byte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Reflection.Method<NbtIo, Object> write = Reflection.getMethod(NbtIo.class, CompoundTag.class, DataOutput.class);
|
private static final Reflection.Method<NbtIo, Void> write = Reflection.getMethod(NbtIo.class, CompoundTag.class, DataOutput.class);
|
||||||
public static CompressedBlockEntity compress(CompoundTag tag, ByteArrayOutputStream baos) {
|
public static CompressedBlockEntity compress(CompoundTag tag, ByteArrayOutputStream baos) {
|
||||||
baos.reset();
|
baos.reset();
|
||||||
DataOutputStream dos = new DataOutputStream(baos);
|
DataOutputStream dos = new DataOutputStream(baos);
|
||||||
|
@ -46,7 +46,7 @@ public class NoVersionTranslator implements VersionTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final PalettedContainer.Strategy SECTION_STATES = Reflection.getField(PalettedContainer.Strategy.class, PalettedContainer.Strategy.class).get(null);
|
private static final PalettedContainer.Strategy SECTION_STATES = Reflection.getField(PalettedContainer.Strategy.class, PalettedContainer.Strategy.class).get(null);
|
||||||
private static final Reflection.Method<PalettedContainer, Object> read = Reflection.getMethod(PalettedContainer.class, FriendlyByteBuf.class);
|
private static final Reflection.Method<PalettedContainer, Void> read = Reflection.getMethod(PalettedContainer.class, FriendlyByteBuf.class);
|
||||||
@Override
|
@Override
|
||||||
public void readPalettedContainer(Player player, ByteBuf buf, PositionConsumer<BlockState> consumer) {
|
public void readPalettedContainer(Player player, ByteBuf buf, PositionConsumer<BlockState> consumer) {
|
||||||
//EMPTY_STATE is completely ignored
|
//EMPTY_STATE is completely ignored
|
||||||
|
@ -7,10 +7,14 @@ import net.minecraft.core.SectionPos;
|
|||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.server.level.ServerChunkCache;
|
import net.minecraft.server.level.ServerChunkCache;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
import net.minecraft.world.level.levelgen.Heightmap;
|
||||||
@ -18,13 +22,14 @@ import net.minecraft.world.level.lighting.LightEngine;
|
|||||||
import net.minecraft.world.level.lighting.LightEventListener;
|
import net.minecraft.world.level.lighting.LightEventListener;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class ChunkSectionModifier {
|
public class ChunkSectionModifier {
|
||||||
|
|
||||||
//TODO multiversioning
|
private static final Reflection.Method<Heightmap.Types, String> getSerializationKey = Reflection.getTypedMethod(Heightmap.Types.class, String.class);
|
||||||
private static final List<Heightmap.Types> types = List.of(Heightmap.Types.WORLD_SURFACE, Heightmap.Types.OCEAN_FLOOR, Heightmap.Types.MOTION_BLOCKING, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
|
private static final List<Heightmap.Types> types = Arrays.stream(Heightmap.Types.values()).filter(type -> List.of("WORLD_SURFACE", "OCEAN_FLOOR", "MOTION_BLOCKING", "MOTION_BLOCKING_NO_LEAVES").contains(getSerializationKey.invoke(type))).toList();
|
||||||
//TODO end multiversioning
|
|
||||||
|
|
||||||
private final int cx;
|
private final int cx;
|
||||||
private final int cy;
|
private final int cy;
|
||||||
@ -37,30 +42,35 @@ public class ChunkSectionModifier {
|
|||||||
private final Heightmap[] heightmaps;
|
private final Heightmap[] heightmaps;
|
||||||
private final boolean hadOnlyAir;
|
private final boolean hadOnlyAir;
|
||||||
|
|
||||||
|
private static final Reflection.Method<ServerChunkCache, ThreadedLevelLightEngine> getLightEngine = Reflection.getTypedMethod(ServerChunkCache.class, ThreadedLevelLightEngine.class);
|
||||||
|
private static final Reflection.Method<ChunkAccess, LevelChunkSection> getSection = Reflection.getTypedMethod(ChunkAccess.class, LevelChunkSection.class, int.class);
|
||||||
|
private static final Reflection.Method<LevelChunkSection, Boolean> hasOnlyAir = Reflection.getTypedMethod(LevelChunkSection.class, boolean.class, 1);
|
||||||
|
private static final Reflection.Field<ChunkAccess, Map> chunkHeightmaps = Reflection.getField(ChunkAccess.class, Map.class);
|
||||||
public ChunkSectionModifier(World world, int cx, int cy, int cz) {
|
public ChunkSectionModifier(World world, int cx, int cy, int cz) {
|
||||||
this.cx = cx;
|
this.cx = cx;
|
||||||
this.cy = cy;
|
this.cy = cy;
|
||||||
this.cz = cz;
|
this.cz = cz;
|
||||||
|
|
||||||
ServerLevel level = AxiomPaper.convert(world);
|
ServerLevel level = AxiomPaper.convert(world);
|
||||||
//TODO multiversioning
|
chunkSource = AxiomPaper.getChunkSource(level);
|
||||||
chunkSource = level.getChunkSource();
|
lightEngine = getLightEngine.invoke(chunkSource);
|
||||||
lightEngine = chunkSource.getLightEngine();
|
chunk = AxiomPaper.getChunk(level, cx, cz);
|
||||||
chunk = level.getChunk(cx, cz);
|
|
||||||
|
|
||||||
section = chunk.getSection(cy - world.getMinHeight() >> 4);
|
section = getSection.invoke(chunk, cy - world.getMinHeight() >> 4);
|
||||||
hadOnlyAir = section.hasOnlyAir();
|
hadOnlyAir = hasOnlyAir.invoke(section);
|
||||||
|
|
||||||
heightmaps = types.stream().map(chunk.heightmaps::get).toArray(Heightmap[]::new);
|
heightmaps = types.stream().map(chunkHeightmaps.get(chunk)::get).toArray(Heightmap[]::new);
|
||||||
//TODO end multiversioning
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Reflection.Method<LevelChunkSection, BlockState> setBlockState = Reflection.getTypedMethod(LevelChunkSection.class, BlockState.class, int.class, int.class, int.class, BlockState.class, boolean.class);
|
||||||
|
private static final Reflection.Method<Heightmap, Boolean> update = Reflection.getTypedMethod(Heightmap.class, boolean.class, int.class, int.class, int.class, BlockState.class);
|
||||||
|
private static final Reflection.Method<BlockState, Block> getBlock = Reflection.getTypedMethod(BlockState.class, Block.class);
|
||||||
|
private static final Reflection.Method<ChunkAccess, Void> removeBlockEntity = Reflection.getMethod(ChunkAccess.class, BlockPos.class);
|
||||||
|
private static final Reflection.Method<ServerChunkCache, Void> blockChanged = Reflection.getMethod(ServerChunkCache.class, BlockPos.class);
|
||||||
|
private static final Reflection.Method<LightEngine, Boolean> hasDifferentLightProperties = Reflection.getTypedMethod(LightEngine.class, boolean.class, BlockGetter.class, BlockPos.class, BlockState.class, BlockState.class);
|
||||||
|
private static final Reflection.Method<LightEventListener, Void> checkBlock = Reflection.getMethod(LightEventListener.class, BlockPos.class);
|
||||||
public void setState(int sx, int sy, int sz, BlockState state) {
|
public void setState(int sx, int sy, int sz, BlockState state) {
|
||||||
//TODO multiversioning
|
BlockState old = setBlockState.invoke(section, sx, sy, sz, state, false);
|
||||||
if (hadOnlyAir && state.isAir())
|
|
||||||
return;
|
|
||||||
|
|
||||||
BlockState old = section.setBlockState(sx, sy, sz, state, false);
|
|
||||||
if (state == old)
|
if (state == old)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -68,68 +78,70 @@ public class ChunkSectionModifier {
|
|||||||
BlockPos pos = new BlockPos(cx * 16 + sx, by, cz * 16 + sz);
|
BlockPos pos = new BlockPos(cx * 16 + sx, by, cz * 16 + sz);
|
||||||
|
|
||||||
for (Heightmap heightmap : heightmaps)
|
for (Heightmap heightmap : heightmaps)
|
||||||
heightmap.update(sx, by, sz, state);
|
update.invoke(heightmap, sx, by, sz, state);
|
||||||
|
|
||||||
if (state.hasBlockEntity()) {
|
if (getBlock.invoke(state) instanceof EntityBlock) {
|
||||||
setBlockEntity(state, pos);
|
setBlockEntity(state, pos);
|
||||||
} else if (old.hasBlockEntity()) {
|
} else if (getBlock.invoke(old) instanceof EntityBlock) {
|
||||||
chunk.removeBlockEntity(pos);
|
removeBlockEntity.invoke(chunk, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
chunkSource.blockChanged(pos);
|
blockChanged.invoke(chunkSource, pos);
|
||||||
|
|
||||||
if (LightEngine.hasDifferentLightProperties(chunk, pos, old, state)) {
|
if (hasDifferentLightProperties.invoke(null, chunk, pos, old, state)) {
|
||||||
lightEngine.checkBlock(pos);
|
checkBlock.invoke(lightEngine, pos);
|
||||||
}
|
}
|
||||||
//TODO end multiversioning
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Reflection.Method<BlockGetter, BlockEntity> getBlockEntity = Reflection.getTypedMethod(BlockGetter.class, BlockEntity.class, BlockPos.class);
|
||||||
|
private static final Reflection.Method<BlockEntity, Void> load = Reflection.getMethod(BlockEntity.class, CompoundTag.class);
|
||||||
public void setBlockEntity(int sx, int sy, int sz, CompoundTag tag) {
|
public void setBlockEntity(int sx, int sy, int sz, CompoundTag tag) {
|
||||||
//TODO multiversioning
|
BlockEntity blockEntity = getBlockEntity.invoke(chunk, new BlockPos(cx * 16 + sx, cy * 16 + sy, cz * 16 + sz));
|
||||||
BlockEntity blockEntity = chunk.getBlockEntity(new BlockPos(cx * 16 + sx, cy * 16 + sy, cz * 16 + sz), LevelChunk.EntityCreationType.CHECK);
|
|
||||||
|
|
||||||
if(blockEntity != null)
|
if(blockEntity != null)
|
||||||
blockEntity.load(tag);
|
load.invoke(blockEntity, tag);
|
||||||
//TODO end multiversioning
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Reflection.Method<LevelChunk, Object> updateBlockEntityTicker = Reflection.getMethod(LevelChunk.class, BlockEntity.class);
|
private static final Reflection.Method<BlockEntity, BlockEntityType> getType = Reflection.getTypedMethod(BlockEntity.class, BlockEntityType.class);
|
||||||
|
private static final Reflection.Method<BlockEntityType, Boolean> isValid = Reflection.getTypedMethod(BlockEntityType.class, boolean.class, BlockState.class);
|
||||||
|
private static final Reflection.Method<BlockEntity, Void> blockEntitySetBlockState = Reflection.getMethod(BlockEntity.class, BlockState.class);
|
||||||
|
private static final Reflection.Method<LevelChunk, Void> updateBlockEntityTicker = Reflection.getMethod(LevelChunk.class, 2, BlockEntity.class);
|
||||||
|
private static final Reflection.Method<EntityBlock, BlockEntity> newBlockEntity = Reflection.getTypedMethod(EntityBlock.class, BlockEntity.class, BlockPos.class, BlockState.class);
|
||||||
|
private static final Reflection.Method<LevelChunk, Void> addAndRegisterBlockEntity = Reflection.getMethod(LevelChunk.class, BlockEntity.class);
|
||||||
private void setBlockEntity(BlockState state, BlockPos pos) {
|
private void setBlockEntity(BlockState state, BlockPos pos) {
|
||||||
//TODO multiversioning
|
BlockEntity blockEntity = getBlockEntity.invoke(chunk, pos, LevelChunk.EntityCreationType.CHECK);
|
||||||
BlockEntity blockEntity = chunk.getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK);
|
|
||||||
|
|
||||||
if (blockEntity != null) {
|
if (blockEntity != null) {
|
||||||
if (blockEntity.getType().isValid(state)) {
|
if (isValid.invoke(getType.invoke(blockEntity), state)) {
|
||||||
// Block entity is here and the type is correct
|
// Block entity is here and the type is correct
|
||||||
blockEntity.setBlockState(state);
|
blockEntitySetBlockState.invoke(blockEntity, state);
|
||||||
|
|
||||||
updateBlockEntityTicker.invoke(chunk, blockEntity);
|
updateBlockEntityTicker.invoke(chunk, blockEntity);
|
||||||
} else {
|
} else {
|
||||||
// Block entity type isn't correct, we need to recreate it
|
// Block entity type isn't correct, we need to recreate it
|
||||||
chunk.removeBlockEntity(pos);
|
removeBlockEntity.invoke(chunk, pos);
|
||||||
blockEntity = null;
|
blockEntity = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blockEntity == null) {
|
if (blockEntity == null) {
|
||||||
// There isn't a block entity here, create it!
|
// There isn't a block entity here, create it!
|
||||||
Block block = state.getBlock();
|
EntityBlock block = (EntityBlock)getBlock.invoke(state);
|
||||||
blockEntity = ((EntityBlock) block).newBlockEntity(pos, state);
|
blockEntity = newBlockEntity.invoke(block, pos, state);
|
||||||
if (blockEntity != null) {
|
if (blockEntity != null) {
|
||||||
chunk.addAndRegisterBlockEntity(blockEntity);
|
addAndRegisterBlockEntity.invoke(chunk, blockEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//TODO end multiversioning
|
|
||||||
}
|
}
|
||||||
|
private static final Reflection.Method<SectionPos, SectionPos> of = Reflection.getTypedMethod(SectionPos.class, SectionPos.class, int.class, int.class, int.class);
|
||||||
|
private static final Reflection.Method<LightEventListener, Void> updateSectionStatus = Reflection.getMethod(LightEventListener.class, SectionPos.class, boolean.class);
|
||||||
|
private static final Reflection.Method<ChunkAccess, Void> setUnsaved = Reflection.getMethod(ChunkAccess.class, boolean.class);
|
||||||
public void finish() {
|
public void finish() {
|
||||||
//TODO multiversioning
|
boolean onlyAir = hasOnlyAir.invoke(section);
|
||||||
boolean hasOnlyAir = section.hasOnlyAir();
|
if (hadOnlyAir != onlyAir) {
|
||||||
if (hadOnlyAir != hasOnlyAir) {
|
updateSectionStatus.invoke(lightEngine, of.invoke(null, cx, cy, cz), onlyAir);
|
||||||
lightEngine.updateSectionStatus(SectionPos.of(cx, cy, cz), hasOnlyAir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk.setUnsaved(true);
|
setUnsaved.invoke(chunk, true);
|
||||||
//TODO end multiversioning
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import io.netty.buffer.ByteBuf;
|
|||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.minecraft.network.Connection;
|
import net.minecraft.network.Connection;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
@ -23,7 +22,6 @@ import java.util.UUID;
|
|||||||
|
|
||||||
public class HelloPacketListener implements AxiomPacketListener, Listener {
|
public class HelloPacketListener implements AxiomPacketListener, Listener {
|
||||||
|
|
||||||
private static final Reflection.Field<ServerPlayer, ServerGamePacketListenerImpl> serverPlayerConnection = Reflection.getField(ServerPlayer.class, ServerGamePacketListenerImpl.class);
|
|
||||||
private static final Reflection.Field<ServerGamePacketListenerImpl, Connection> packetListenerConnection = Reflection.getField(ServerGamePacketListenerImpl.class, Connection.class);
|
private static final Reflection.Field<ServerGamePacketListenerImpl, Connection> packetListenerConnection = Reflection.getField(ServerGamePacketListenerImpl.class, Connection.class);
|
||||||
private static final Reflection.Field<Connection, Channel> channel = Reflection.getField(Connection.class, Channel.class);
|
private static final Reflection.Field<Connection, Channel> channel = Reflection.getField(Connection.class, Channel.class);
|
||||||
|
|
||||||
@ -54,7 +52,7 @@ public class HelloPacketListener implements AxiomPacketListener, Listener {
|
|||||||
// Welcome to multiversioning!
|
// Welcome to multiversioning!
|
||||||
channel.get(
|
channel.get(
|
||||||
packetListenerConnection.get(
|
packetListenerConnection.get(
|
||||||
serverPlayerConnection.get(
|
AxiomPaper.getConnection(
|
||||||
AxiomPaper.convert(player)
|
AxiomPaper.convert(player)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -2,6 +2,7 @@ package com.moulberry.axiom.packet;
|
|||||||
|
|
||||||
import com.moulberry.axiom.AxiomConstants;
|
import com.moulberry.axiom.AxiomConstants;
|
||||||
import com.moulberry.axiom.AxiomPaper;
|
import com.moulberry.axiom.AxiomPaper;
|
||||||
|
import com.moulberry.axiom.Reflection;
|
||||||
import com.moulberry.axiom.buffer.BiomeBuffer;
|
import com.moulberry.axiom.buffer.BiomeBuffer;
|
||||||
import com.moulberry.axiom.buffer.CompressedBlockEntity;
|
import com.moulberry.axiom.buffer.CompressedBlockEntity;
|
||||||
import com.moulberry.axiom.buffer.MojBuf;
|
import com.moulberry.axiom.buffer.MojBuf;
|
||||||
@ -10,17 +11,18 @@ import com.moulberry.axiom.integration.RegionProtection;
|
|||||||
import com.moulberry.axiom.integration.VersionTranslator;
|
import com.moulberry.axiom.integration.VersionTranslator;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.network.protocol.Packet;
|
||||||
import net.minecraft.network.protocol.game.ClientboundChunksBiomesPacket;
|
import net.minecraft.network.protocol.game.ClientboundChunksBiomesPacket;
|
||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
|
import net.minecraft.server.level.ServerChunkCache;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.server.network.ServerPlayerConnection;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Chunk;
|
import org.bukkit.Chunk;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.CraftChunk;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -78,6 +80,10 @@ public class SetBlockBufferPacketListener implements AxiomPacketListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Reflection.Field<ServerChunkCache, ChunkMap> chunkMap = Reflection.getField(ServerChunkCache.class, ChunkMap.class);
|
||||||
|
private static final Reflection.Method<ChunkMap, List> getPlayers = Reflection.getTypedMethod(ChunkMap.class, List.class, ChunkPos.class, boolean.class);
|
||||||
|
private static final Reflection.Method<ServerPlayerConnection, Void> send = Reflection.getMethod(ServerPlayerConnection.class, Packet.class);
|
||||||
|
private static final Reflection.Method<ClientboundChunksBiomesPacket, ClientboundChunksBiomesPacket> forChunks = Reflection.getTypedMethod(ClientboundChunksBiomesPacket.class, ClientboundChunksBiomesPacket.class, List.class);
|
||||||
private void applyBiomeBuffer(World world, BiomeBuffer biomeBuffer) {
|
private void applyBiomeBuffer(World world, BiomeBuffer biomeBuffer) {
|
||||||
Set<Chunk> changedChunks = new HashSet<>();
|
Set<Chunk> changedChunks = new HashSet<>();
|
||||||
biomeBuffer.forEachEntry((x, y, z, biome) -> {
|
biomeBuffer.forEachEntry((x, y, z, biome) -> {
|
||||||
@ -86,17 +92,16 @@ public class SetBlockBufferPacketListener implements AxiomPacketListener {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ServerLevel level = AxiomPaper.convert(world);
|
ServerLevel level = AxiomPaper.convert(world);
|
||||||
//TODO multiversioning
|
ChunkMap map = chunkMap.get(AxiomPaper.getChunkSource(level));
|
||||||
ChunkMap chunkMap = level.getChunkSource().chunkMap;
|
HashMap<ServerPlayer, List<LevelChunk>> playerChunks = new HashMap<>();
|
||||||
HashMap<ServerPlayer, List<LevelChunk>> map = new HashMap<>();
|
|
||||||
for (Chunk chunk : changedChunks) {
|
for (Chunk chunk : changedChunks) {
|
||||||
ChunkPos chunkPos = new ChunkPos(chunk.getX(), chunk.getZ());
|
ChunkPos chunkPos = new ChunkPos(chunk.getX(), chunk.getZ());
|
||||||
for (ServerPlayer serverPlayer2 : chunkMap.getPlayers(chunkPos, false)) {
|
LevelChunk nativeChunk = AxiomPaper.getChunk(level, chunk.getX(), chunk.getZ());
|
||||||
map.computeIfAbsent(serverPlayer2, serverPlayer -> new ArrayList<>()).add((LevelChunk) ((CraftChunk)chunk).getHandle(ChunkStatus.BIOMES));
|
for (ServerPlayer player : (List<ServerPlayer>)getPlayers.invoke(map, chunkPos, false)) {
|
||||||
|
playerChunks.computeIfAbsent(player, p -> new ArrayList<>()).add(nativeChunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
map.forEach((serverPlayer, list) -> serverPlayer.connection.send(ClientboundChunksBiomesPacket.forChunks(list)));
|
playerChunks.forEach((serverPlayer, list) -> send.invoke(AxiomPaper.getConnection(serverPlayer), forChunks.invoke(null, list)));
|
||||||
//TODO end multiversioning
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,38 @@
|
|||||||
package com.moulberry.axiom.packet;
|
package com.moulberry.axiom.packet;
|
||||||
|
|
||||||
import com.moulberry.axiom.AxiomPaper;
|
import com.moulberry.axiom.AxiomPaper;
|
||||||
|
import com.moulberry.axiom.Reflection;
|
||||||
import com.moulberry.axiom.buffer.MojBuf;
|
import com.moulberry.axiom.buffer.MojBuf;
|
||||||
import com.moulberry.axiom.event.AxiomModifyWorldEvent;
|
import com.moulberry.axiom.event.AxiomModifyWorldEvent;
|
||||||
|
import com.moulberry.axiom.integration.RegionProtection;
|
||||||
import com.moulberry.axiom.integration.VersionTranslator;
|
import com.moulberry.axiom.integration.VersionTranslator;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Vec3i;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class SetBlockPacketListener implements AxiomPacketListener {
|
public class SetBlockPacketListener implements AxiomPacketListener {
|
||||||
|
|
||||||
|
private static final Reflection.Method<Level, Boolean> setBlock = Reflection.getTypedMethod(Level.class, boolean.class, BlockPos.class, BlockState.class, int.class);
|
||||||
|
|
||||||
|
private static final Reflection.Method<Vec3i, Integer> getX = Reflection.getTypedMethod(Vec3i.class, int.class, 0);
|
||||||
|
private static final Reflection.Method<Vec3i, Integer> getY = Reflection.getTypedMethod(Vec3i.class, int.class, 1);
|
||||||
|
private static final Reflection.Method<Vec3i, Integer> getZ = Reflection.getTypedMethod(Vec3i.class, int.class, 2);
|
||||||
|
|
||||||
|
private static final Reflection.Method<ServerGamePacketListenerImpl, Void> ackBlockChangesUpTo = Reflection.getMethod(ServerGamePacketListenerImpl.class, int.class);
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) {
|
public void onMessage(@NotNull Player player, @NotNull ByteBuf buf) {
|
||||||
|
World world = player.getWorld();
|
||||||
|
|
||||||
// Check if player is allowed to modify this world
|
// Check if player is allowed to modify this world
|
||||||
AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(player, player.getWorld());
|
AxiomModifyWorldEvent modifyWorldEvent = new AxiomModifyWorldEvent(player, world);
|
||||||
Bukkit.getPluginManager().callEvent(modifyWorldEvent);
|
Bukkit.getPluginManager().callEvent(modifyWorldEvent);
|
||||||
if (modifyWorldEvent.isCancelled()) return;
|
if (modifyWorldEvent.isCancelled()) return;
|
||||||
|
|
||||||
@ -28,21 +43,24 @@ public class SetBlockPacketListener implements AxiomPacketListener {
|
|||||||
int sequenceId = buf.readInt();
|
int sequenceId = buf.readInt();
|
||||||
|
|
||||||
ServerPlayer serverPlayer = AxiomPaper.convert(player);
|
ServerPlayer serverPlayer = AxiomPaper.convert(player);
|
||||||
|
int x = getX.invoke(pos);
|
||||||
|
int y = getY.invoke(pos);
|
||||||
|
int z = getZ.invoke(pos);
|
||||||
|
|
||||||
// Update blocks
|
// Update blocks
|
||||||
//TODO multiversioning
|
if(RegionProtection.getProtection.apply(player, world).canBuildInSection(x >> 4, y >> 4, z >> 4)) {
|
||||||
if (updateNeighbors) {
|
if (updateNeighbors) {
|
||||||
AxiomPaper.convert(player.getWorld()).setBlock(pos, state, 3);
|
setBlock.invoke(AxiomPaper.convert(player.getWorld()), pos, state, 3);
|
||||||
} else {
|
} else {
|
||||||
ChunkSectionModifier section = new ChunkSectionModifier(player.getWorld(), pos.getX() >> 4, pos.getY() >> 4, pos.getZ() >> 4);
|
ChunkSectionModifier section = new ChunkSectionModifier(player.getWorld(), x >> 4, y >> 4, z >> 4);
|
||||||
section.setState(pos.getX() & 0xF, pos.getY() & 0xF, pos.getZ() & 0xF, state);
|
section.setState(x & 0xF, y & 0xF, z & 0xF, state);
|
||||||
section.finish();
|
section.finish();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (sequenceId >= 0) {
|
if (sequenceId >= 0) {
|
||||||
serverPlayer.connection.ackBlockChangesUpTo(sequenceId);
|
ackBlockChangesUpTo.invoke(AxiomPaper.getConnection(serverPlayer), sequenceId);
|
||||||
}
|
}
|
||||||
//TODO end multiversioning
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ public class ItemStackDataType implements PersistentDataType<PersistentDataConta
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final Class<PersistentDataContainer> CraftPersistentDataContainer = Reflection.getClass("org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer");
|
private static final Class<PersistentDataContainer> CraftPersistentDataContainer = Reflection.getClass("org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer");
|
||||||
private static final Reflection.Method<PersistentDataContainer, Object> putAll = Reflection.getMethod(CraftPersistentDataContainer, "putAll", CompoundTag.class);
|
private static final Reflection.Method<PersistentDataContainer, Void> putAll = Reflection.getMethod(CraftPersistentDataContainer, "putAll", CompoundTag.class);
|
||||||
private static final Reflection.Method<PersistentDataContainer, CompoundTag> toTagCompound = Reflection.getTypedMethod(CraftPersistentDataContainer, "toTagCompound", CompoundTag.class);
|
private static final Reflection.Method<PersistentDataContainer, CompoundTag> toTagCompound = Reflection.getTypedMethod(CraftPersistentDataContainer, "toTagCompound", CompoundTag.class);
|
||||||
|
|
||||||
private static final Reflection.Method<net.minecraft.world.item.ItemStack, CompoundTag> save = Reflection.getTypedMethod(net.minecraft.world.item.ItemStack.class, CompoundTag.class, CompoundTag.class);
|
private static final Reflection.Method<net.minecraft.world.item.ItemStack, CompoundTag> save = Reflection.getTypedMethod(net.minecraft.world.item.ItemStack.class, CompoundTag.class, CompoundTag.class);
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren