Full Multiversion (untested)
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful

Dieser Commit ist enthalten in:
Lixfel 2023-09-12 17:32:07 +02:00
Ursprung 8cfed96b39
Commit a0367320c3
9 geänderte Dateien mit 133 neuen und 78 gelöschten Zeilen

Datei anzeigen

@ -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) {

Datei anzeigen

@ -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) {

Datei anzeigen

@ -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);

Datei anzeigen

@ -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

Datei anzeigen

@ -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
} }
} }

Datei anzeigen

@ -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)
) )
) )

Datei anzeigen

@ -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
} }
} }

Datei anzeigen

@ -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
} }
} }

Datei anzeigen

@ -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);