diff --git a/src/main/java/ac/grim/grimac/events/packets/PacketServerTags.java b/src/main/java/ac/grim/grimac/events/packets/PacketServerTags.java new file mode 100644 index 0000000000..8e887cacfc --- /dev/null +++ b/src/main/java/ac/grim/grimac/events/packets/PacketServerTags.java @@ -0,0 +1,28 @@ +package ac.grim.grimac.events.packets; + +import ac.grim.grimac.GrimAPI; +import ac.grim.grimac.player.GrimPlayer; +import com.github.retrooper.packetevents.event.PacketListenerAbstract; +import com.github.retrooper.packetevents.event.PacketSendEvent; +import com.github.retrooper.packetevents.protocol.packettype.PacketType; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerTags; + +public class PacketServerTags extends PacketListenerAbstract { + + @Override + public void onPacketSend(PacketSendEvent event) { + if (event.getPacketType() == PacketType.Play.Server.TAGS || event.getPacketType() == PacketType.Configuration.Server.UPDATE_TAGS) { + GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser()); + if (player == null) return; + + WrapperPlayServerTags tags = new WrapperPlayServerTags(event); + final boolean isPlay = event.getPacketType() == PacketType.Play.Server.TAGS; + if (isPlay) { + player.latencyUtils.addRealTimeTask(player.lastTransactionSent.get(), () -> player.tagManager.handleTagSync(tags)); + } else { + // This is during configuration stage, player isn't even in the game yet so no need to lag compensate. + player.tagManager.handleTagSync(tags); + } + } + } +} diff --git a/src/main/java/ac/grim/grimac/manager/init/start/PacketManager.java b/src/main/java/ac/grim/grimac/manager/init/start/PacketManager.java index fd5a8bcbdf..e5f9aae25e 100644 --- a/src/main/java/ac/grim/grimac/manager/init/start/PacketManager.java +++ b/src/main/java/ac/grim/grimac/manager/init/start/PacketManager.java @@ -30,6 +30,10 @@ public void start() { PacketEvents.getAPI().getEventManager().registerListener(new CheckManagerListener()); PacketEvents.getAPI().getEventManager().registerListener(new PacketPlayerSteer()); + if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)) { + PacketEvents.getAPI().getEventManager().registerListener(new PacketServerTags()); + } + if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_18)) { PacketEvents.getAPI().getEventManager().registerListener(new PacketWorldReaderEighteen()); } else if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_8_8)) { diff --git a/src/main/java/ac/grim/grimac/player/GrimPlayer.java b/src/main/java/ac/grim/grimac/player/GrimPlayer.java index 89ea7948fe..106110eed1 100644 --- a/src/main/java/ac/grim/grimac/player/GrimPlayer.java +++ b/src/main/java/ac/grim/grimac/player/GrimPlayer.java @@ -16,6 +16,7 @@ import ac.grim.grimac.utils.data.*; import ac.grim.grimac.utils.data.packetentity.PacketEntity; import ac.grim.grimac.utils.data.packetentity.PacketEntitySelf; +import ac.grim.grimac.utils.data.tags.SyncedTags; import ac.grim.grimac.utils.enums.FluidTag; import ac.grim.grimac.utils.enums.Pose; import ac.grim.grimac.utils.latency.*; @@ -85,6 +86,7 @@ public class GrimPlayer implements GrimUser { public ActionManager actionManager; public PunishmentManager punishmentManager; public MovementCheckRunner movementCheckRunner; + public SyncedTags tagManager; // End manager like classes public Vector clientVelocity = new Vector(); PacketTracker packetTracker; @@ -225,6 +227,7 @@ public GrimPlayer(User user) { actionManager = new ActionManager(this); checkManager = new CheckManager(this); punishmentManager = new PunishmentManager(this); + tagManager = new SyncedTags(this); movementCheckRunner = new MovementCheckRunner(this); compensatedWorld = new CompensatedWorld(this); diff --git a/src/main/java/ac/grim/grimac/predictionengine/PointThreeEstimator.java b/src/main/java/ac/grim/grimac/predictionengine/PointThreeEstimator.java index 80fd93c24c..bed899ce36 100644 --- a/src/main/java/ac/grim/grimac/predictionengine/PointThreeEstimator.java +++ b/src/main/java/ac/grim/grimac/predictionengine/PointThreeEstimator.java @@ -6,6 +6,7 @@ import ac.grim.grimac.utils.collisions.datatypes.CollisionBox; import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox; import ac.grim.grimac.utils.data.VectorData; +import ac.grim.grimac.utils.data.tags.SyncedTags; import ac.grim.grimac.utils.nmsutil.*; import com.github.retrooper.packetevents.protocol.item.type.ItemTypes; import com.github.retrooper.packetevents.protocol.player.ClientVersion; @@ -13,6 +14,7 @@ import com.github.retrooper.packetevents.protocol.potion.PotionTypes; import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState; import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags; +import com.github.retrooper.packetevents.protocol.world.states.type.StateType; import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes; import lombok.Getter; import lombok.Setter; @@ -119,7 +121,8 @@ public PointThreeEstimator(GrimPlayer player) { // Handle game events that occur between skipped ticks - thanks a lot mojang for removing the idle packet! public void handleChangeBlock(int x, int y, int z, WrappedBlockState state) { - CollisionBox data = CollisionData.getData(state.getType()).getMovementCollisionBox(player, player.getClientVersion(), state, x, y, z); + final StateType stateType = state.getType(); + CollisionBox data = CollisionData.getData(stateType).getMovementCollisionBox(player, player.getClientVersion(), state, x, y, z); SimpleCollisionBox normalBox = GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.x, player.y, player.z, 0.6f, 1.8f); // Calculate head hitters. Take a shortcut by checking if the player doesn't intersect with this block, but does @@ -130,10 +133,10 @@ public void handleChangeBlock(int x, int y, int z, WrappedBlockState state) { } SimpleCollisionBox pointThreeBox = GetBoundingBox.getBoundingBoxFromPosAndSize(player, player.x, player.y - 0.03, player.z, 0.66f, 1.86f); - if ((Materials.isWater(player.getClientVersion(), state) || state.getType() == StateTypes.LAVA) && + if ((Materials.isWater(player.getClientVersion(), state) || stateType == StateTypes.LAVA) && pointThreeBox.isIntersected(new SimpleCollisionBox(x, y, z))) { - if (state.getType() == StateTypes.BUBBLE_COLUMN) { + if (stateType == StateTypes.BUBBLE_COLUMN) { isNearBubbleColumn = true; } @@ -164,8 +167,8 @@ public void handleChangeBlock(int x, int y, int z, WrappedBlockState state) { } } - if (!player.compensatedEntities.getSelf().inVehicle() && ((state.getType() == StateTypes.POWDER_SNOW && player.getInventory().getBoots().getType() == ItemTypes.LEATHER_BOOTS) - || Materials.isClimbable(state.getType())) && pointThreeBox.isIntersected(new SimpleCollisionBox(x, y, z))) { + if (!player.compensatedEntities.getSelf().inVehicle() && ((stateType == StateTypes.POWDER_SNOW && player.getInventory().getBoots().getType() == ItemTypes.LEATHER_BOOTS) + || player.tagManager.block(SyncedTags.CLIMBABLE).contains(stateType)) && pointThreeBox.isIntersected(new SimpleCollisionBox(x, y, z))) { isNearClimbable = true; } } @@ -245,16 +248,17 @@ private void checkNearbyBlocks(SimpleCollisionBox pointThreeBox) { // Check for flowing water Collisions.hasMaterial(player, pointThreeBox, (pair) -> { - WrappedBlockState state = pair.getFirst(); - if (Materials.isClimbable(state.getType()) || (state.getType() == StateTypes.POWDER_SNOW && !player.compensatedEntities.getSelf().inVehicle() && player.getInventory().getBoots().getType() == ItemTypes.LEATHER_BOOTS)) { + final WrappedBlockState state = pair.getFirst(); + final StateType stateType = state.getType(); + if (player.tagManager.block(SyncedTags.CLIMBABLE).contains(stateType) || (stateType == StateTypes.POWDER_SNOW && !player.compensatedEntities.getSelf().inVehicle() && player.getInventory().getBoots().getType() == ItemTypes.LEATHER_BOOTS)) { isNearClimbable = true; } - if (BlockTags.TRAPDOORS.contains(state.getType())) { + if (BlockTags.TRAPDOORS.contains(stateType)) { isNearClimbable = isNearClimbable || Collisions.trapdoorUsableAsLadder(player, pair.getSecond().getX(), pair.getSecond().getY(), pair.getSecond().getZ(), state); } - if (state.getType() == StateTypes.BUBBLE_COLUMN) { + if (stateType == StateTypes.BUBBLE_COLUMN) { isNearBubbleColumn = true; } diff --git a/src/main/java/ac/grim/grimac/utils/data/tags/SyncedTag.java b/src/main/java/ac/grim/grimac/utils/data/tags/SyncedTag.java new file mode 100644 index 0000000000..c432e2b68a --- /dev/null +++ b/src/main/java/ac/grim/grimac/utils/data/tags/SyncedTag.java @@ -0,0 +1,66 @@ +package ac.grim.grimac.utils.data.tags; + +import com.github.retrooper.packetevents.resources.ResourceLocation; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerTags; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; + +public final class SyncedTag { + + private final ResourceLocation location; + private final Set values; + private final Function remapper; + + private SyncedTag(ResourceLocation location, Function remapper, Set defaultValues) { + this.location = location; + this.values = new HashSet<>(); + this.remapper = remapper; + this.values.addAll(defaultValues); + } + + public static Builder builder(ResourceLocation location) { + return new Builder<>(location); + } + + public ResourceLocation location() { + return location; + } + + public boolean contains(T value) { + return values.contains(value); + } + + public void readTagValues(WrapperPlayServerTags.Tag tag) { + // Server is sending tag replacement, clear default values. + values.clear(); + for (int id : tag.getValues()) { + values.add(remapper.apply(id)); + } + } + + public static final class Builder { + private final ResourceLocation location; + private Function remapper; + private Set defaultValues; + + private Builder(ResourceLocation location) { + this.location = location; + } + + public Builder remapper(Function remapper) { + this.remapper = remapper; + return this; + } + + public Builder defaults(Set defaultValues) { + this.defaultValues = defaultValues; + return this; + } + + public SyncedTag build() { + return new SyncedTag<>(location, remapper, defaultValues); + } + } +} diff --git a/src/main/java/ac/grim/grimac/utils/data/tags/SyncedTags.java b/src/main/java/ac/grim/grimac/utils/data/tags/SyncedTags.java new file mode 100644 index 0000000000..7ed2403cba --- /dev/null +++ b/src/main/java/ac/grim/grimac/utils/data/tags/SyncedTags.java @@ -0,0 +1,65 @@ +package ac.grim.grimac.utils.data.tags; + +import ac.grim.grimac.player.GrimPlayer; +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.manager.server.ServerVersion; +import com.github.retrooper.packetevents.protocol.player.ClientVersion; +import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags; +import com.github.retrooper.packetevents.protocol.world.states.type.StateType; +import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes; +import com.github.retrooper.packetevents.resources.ResourceLocation; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerTags; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +/** + * This class stores tags that the client is aware of. + */ +public final class SyncedTags { + + private static final ServerVersion VERSION = PacketEvents.getAPI().getServerManager().getVersion(); + + private static final ResourceLocation BLOCK = VERSION.isNewerThanOrEquals(ServerVersion.V_1_21) ? ResourceLocation.minecraft("block") : ResourceLocation.minecraft("blocks"); + + public static final ResourceLocation CLIMBABLE = ResourceLocation.minecraft("climbable"); + + private final GrimPlayer player; + private final Map>> synced; + + public SyncedTags(GrimPlayer player) { + this.player = player; + this.synced = new HashMap<>(); + trackTags(BLOCK, id -> StateTypes.getById(player.getClientVersion(), id), + SyncedTag.builder(CLIMBABLE).defaults(BlockTags.CLIMBABLE.getStates())); + } + + @SafeVarargs + private final void trackTags(ResourceLocation location, Function remapper, SyncedTag.Builder... syncedTags) { + final Map> tags = new HashMap<>(syncedTags.length); + for (SyncedTag.Builder syncedTag : syncedTags) { + syncedTag.remapper(remapper); + final SyncedTag built = syncedTag.build(); + tags.put(built.location(), built); + } + synced.put(location, tags); + } + + public SyncedTag block(ResourceLocation tag) { + final Map> blockTags = synced.get(BLOCK); + return (SyncedTag) blockTags.get(tag); + } + + public void handleTagSync(WrapperPlayServerTags tags) { + if (player.getClientVersion().isOlderThan(ClientVersion.V_1_13)) return; + tags.getTagMap().forEach((location, tagList) -> { + if (!synced.containsKey(location)) return; + final Map> syncedTags = synced.get(location); + tagList.forEach(tag -> { + if (!syncedTags.containsKey(tag.getKey())) return; + syncedTags.get(tag.getKey()).readTagValues(tag); + }); + }); + } +} diff --git a/src/main/java/ac/grim/grimac/utils/nmsutil/Collisions.java b/src/main/java/ac/grim/grimac/utils/nmsutil/Collisions.java index da91be6e9e..622fc7b442 100644 --- a/src/main/java/ac/grim/grimac/utils/nmsutil/Collisions.java +++ b/src/main/java/ac/grim/grimac/utils/nmsutil/Collisions.java @@ -8,6 +8,7 @@ import ac.grim.grimac.utils.collisions.datatypes.SimpleCollisionBox; import ac.grim.grimac.utils.data.Pair; import ac.grim.grimac.utils.data.VectorData; +import ac.grim.grimac.utils.data.tags.SyncedTags; import ac.grim.grimac.utils.latency.CompensatedWorld; import ac.grim.grimac.utils.math.GrimMath; import ac.grim.grimac.utils.math.VectorUtils; @@ -718,7 +719,7 @@ public static boolean onClimbable(GrimPlayer player, double x, double y, double return player.getClientVersion().isNewerThanOrEquals(ClientVersion.V_1_17); } - if (BlockTags.CLIMBABLE.contains(blockMaterial)) { + if (player.tagManager.block(SyncedTags.CLIMBABLE).contains(blockMaterial)) { return true; } diff --git a/src/main/java/ac/grim/grimac/utils/nmsutil/Materials.java b/src/main/java/ac/grim/grimac/utils/nmsutil/Materials.java index f9c000b642..d9925fc3da 100644 --- a/src/main/java/ac/grim/grimac/utils/nmsutil/Materials.java +++ b/src/main/java/ac/grim/grimac/utils/nmsutil/Materials.java @@ -152,10 +152,6 @@ public static boolean isGlassPane(StateType type) { return PANES.contains(type); } - public static boolean isClimbable(StateType type) { - return BlockTags.CLIMBABLE.contains(type); - } - public static boolean isCauldron(StateType type) { return BlockTags.CAULDRONS.contains(type); }