diff --git a/build.gradle.kts b/build.gradle.kts
index d9df0f1..b2c4df3 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -47,8 +47,14 @@ tasks {
archiveFileName = "${rootProject.name}-${ext["versionNoHash"]}.jar"
archiveClassifier = null
- relocate("net.kyori.adventure.text.serializer.gson", "io.github.retrooper.packetevents.adventure.serializer.gson")
- relocate("net.kyori.adventure.text.serializer.legacy", "io.github.retrooper.packetevents.adventure.serializer.legacy")
+ relocate(
+ "net.kyori.adventure.text.serializer.gson",
+ "io.github.retrooper.packetevents.adventure.serializer.gson"
+ )
+ relocate(
+ "net.kyori.adventure.text.serializer.legacy",
+ "io.github.retrooper.packetevents.adventure.serializer.legacy"
+ )
}
assemble {
diff --git a/common/build.gradle.kts b/common/build.gradle.kts
index 440e95a..02a2bbf 100644
--- a/common/build.gradle.kts
+++ b/common/build.gradle.kts
@@ -10,6 +10,7 @@ dependencies {
compileOnlyApi(libs.bundles.adventure)
compileOnlyApi(libs.snakeyaml)
compileOnlyApi(libs.lombok)
+ compileOnly(libs.guava)
annotationProcessor(libs.lombok)
}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/AHIPlatform.java b/common/src/main/java/com/deathmotion/antihealthindicator/AHIPlatform.java
index 80e0e75..d0a13aa 100644
--- a/common/src/main/java/com/deathmotion/antihealthindicator/AHIPlatform.java
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/AHIPlatform.java
@@ -21,7 +21,12 @@
import com.deathmotion.antihealthindicator.api.AntiHealthIndicator;
import com.deathmotion.antihealthindicator.commands.AntiHealthIndicatorCommand;
import com.deathmotion.antihealthindicator.interfaces.Scheduler;
-import com.deathmotion.antihealthindicator.managers.*;
+import com.deathmotion.antihealthindicator.managers.ConfigManager;
+import com.deathmotion.antihealthindicator.managers.LogManager;
+import com.deathmotion.antihealthindicator.managers.PlayerDataManager;
+import com.deathmotion.antihealthindicator.packets.PacketPlayerJoinQuit;
+import com.deathmotion.antihealthindicator.packets.SpoofManagerPacketListener;
+import com.deathmotion.antihealthindicator.util.UpdateChecker;
import com.github.retrooper.packetevents.PacketEvents;
import lombok.Getter;
import net.kyori.adventure.text.Component;
@@ -31,14 +36,22 @@
@Getter
public abstract class AHIPlatform
{
+
+ @Getter
+ private static AHIPlatform> instance;
+
protected ConfigManager
configManager;
protected LogManager
logManager;
protected Scheduler scheduler;
protected AntiHealthIndicatorCommand
command;
- private CacheManager
cacheManager;
+ protected PlayerDataManager
playerDataManager;
+
+ private UpdateChecker
updateChecker;
public void commonOnInitialize() {
+ instance = this;
+
logManager = new LogManager<>(this);
configManager = new ConfigManager<>(this);
AntiHealthIndicator.setAPI(new AntiHealthIndicatorAPIImpl<>(this));
@@ -48,11 +61,13 @@ public void commonOnInitialize() {
* Called when the platform is enabled.
*/
public void commonOnEnable() {
- cacheManager = new CacheManager<>(this);
command = new AntiHealthIndicatorCommand<>(this);
+ playerDataManager = new PlayerDataManager<>(this);
+
+ PacketEvents.getAPI().getEventManager().registerListener(new PacketPlayerJoinQuit<>(this));
+ PacketEvents.getAPI().getEventManager().registerListener(new SpoofManagerPacketListener<>(this));
- new UpdateManager<>(this);
- new PacketManager<>(this);
+ this.updateChecker = new UpdateChecker<>(this);
}
/**
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/cache/EntityCache.java b/common/src/main/java/com/deathmotion/antihealthindicator/cache/EntityCache.java
new file mode 100644
index 0000000..a507fa7
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/cache/EntityCache.java
@@ -0,0 +1,98 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.cache;
+
+import com.deathmotion.antihealthindicator.cache.entities.CachedEntity;
+import com.deathmotion.antihealthindicator.cache.entities.RidableEntity;
+import com.deathmotion.antihealthindicator.cache.trackers.EntityTracker;
+import com.deathmotion.antihealthindicator.cache.trackers.VehicleTracker;
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
+import com.github.retrooper.packetevents.event.PacketSendEvent;
+import lombok.Getter;
+import lombok.NonNull;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Getter
+public class EntityCache {
+ private final AHIPlayer player;
+ private final ConcurrentHashMap cache;
+ private final EntityTracker entityTracker;
+ private final VehicleTracker vehicleTracker;
+
+ public EntityCache(AHIPlayer player) {
+ this.player = player;
+ this.cache = new ConcurrentHashMap<>();
+ this.entityTracker = new EntityTracker(player, this);
+ this.vehicleTracker = new VehicleTracker(player, this);
+ }
+
+ public void onPacketSend(PacketSendEvent event) {
+ entityTracker.onPacketSend(event);
+ vehicleTracker.onPacketSend(event);
+ }
+
+ public Optional getCachedEntity(int entityId) {
+ return Optional.ofNullable(cache.get(entityId));
+ }
+
+ public Optional getVehicleData(int entityId) {
+ return getCachedEntity(entityId)
+ .filter(entityData -> entityData instanceof RidableEntity)
+ .map(entityData -> (RidableEntity) entityData);
+ }
+
+ public void addLivingEntity(int entityId, @NonNull CachedEntity cachedEntity) {
+ cache.put(entityId, cachedEntity);
+ }
+
+ public void removeEntity(int entityId) {
+ cache.remove(entityId);
+ }
+
+ public void resetUserCache() {
+ cache.clear();
+ }
+
+ public void updateVehiclePassenger(int entityId, int passengerId) {
+ getVehicleData(entityId).ifPresent(ridableEntityData -> ridableEntityData.setPassengerId(passengerId));
+ }
+
+ public float getVehicleHealth(int entityId) {
+ return getVehicleData(entityId).map(RidableEntity::getHealth).orElse(0.5f);
+ }
+
+ public boolean isUserPassenger(int entityId) {
+ return getVehicleData(entityId).map(ridableEntityData -> ridableEntityData.getPassengerId() == player.user.getEntityId()).orElse(false);
+ }
+
+ public int getPassengerId(int entityId) {
+ return getVehicleData(entityId).map(RidableEntity::getPassengerId).orElse(0);
+ }
+
+ public int getEntityIdByPassengerId(int passengerId) {
+ return cache.entrySet().stream()
+ .filter(entry -> entry.getValue() instanceof RidableEntity && ((RidableEntity) entry.getValue()).getPassengerId() == passengerId)
+ .map(Map.Entry::getKey)
+ .findFirst()
+ .orElse(0);
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/cache/entities/CachedEntity.java b/common/src/main/java/com/deathmotion/antihealthindicator/cache/entities/CachedEntity.java
new file mode 100644
index 0000000..ce417c9
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/cache/entities/CachedEntity.java
@@ -0,0 +1,34 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.cache.entities;
+
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
+import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
+import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class CachedEntity {
+ private EntityType entityType;
+
+ public void processMetaData(EntityData metaData, AHIPlayer player) {
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/cache/entities/RidableEntity.java b/common/src/main/java/com/deathmotion/antihealthindicator/cache/entities/RidableEntity.java
new file mode 100644
index 0000000..cec2af6
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/cache/entities/RidableEntity.java
@@ -0,0 +1,38 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.cache.entities;
+
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
+import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class RidableEntity extends CachedEntity {
+ private float health;
+ private int passengerId;
+
+ @Override
+ public void processMetaData(EntityData metaData, AHIPlayer player) {
+ if (metaData.getIndex() == player.metadataIndex.HEALTH) {
+ setHealth((float) metaData.getValue());
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/cache/entities/WolfEntity.java b/common/src/main/java/com/deathmotion/antihealthindicator/cache/entities/WolfEntity.java
new file mode 100644
index 0000000..9ce9c8d
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/cache/entities/WolfEntity.java
@@ -0,0 +1,67 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.cache.entities;
+
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
+import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Optional;
+import java.util.UUID;
+
+@Getter
+@Setter
+public class WolfEntity extends CachedEntity {
+ private boolean isTamed;
+ private UUID ownerUUID;
+
+ public boolean isOwnerPresent() {
+ return ownerUUID != null;
+ }
+
+ public UUID getOwnerUUID() {
+ if (!isOwnerPresent()) {
+ throw new IllegalStateException("Owner UUID not present");
+ }
+ return ownerUUID;
+ }
+
+ @Override
+ public void processMetaData(EntityData metaData, AHIPlayer player) {
+ int index = metaData.getIndex();
+
+ if (index == player.metadataIndex.TAMABLE_TAMED) {
+ setTamed(((Byte) metaData.getValue() & 0x04) != 0);
+ } else if (index == player.metadataIndex.TAMABLE_OWNER) {
+ Object value = metaData.getValue();
+
+ UUID ownerUUID = value instanceof String
+ ? Optional.of((String) value)
+ .filter(player.uuid.toString()::equals)
+ .map(UUID::fromString)
+ .orElse(null)
+ : ((Optional) value)
+ .filter(player.uuid::equals)
+ .orElse(null);
+
+ setOwnerUUID(ownerUUID);
+ }
+ }
+}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/EntityTracker.java b/common/src/main/java/com/deathmotion/antihealthindicator/cache/trackers/EntityTracker.java
similarity index 53%
rename from common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/EntityTracker.java
rename to common/src/main/java/com/deathmotion/antihealthindicator/cache/trackers/EntityTracker.java
index 99bd8f1..8119dda 100644
--- a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/EntityTracker.java
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/cache/trackers/EntityTracker.java
@@ -1,34 +1,33 @@
/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
-package com.deathmotion.antihealthindicator.packetlisteners;
+package com.deathmotion.antihealthindicator.cache.trackers;
import com.deathmotion.antihealthindicator.AHIPlatform;
+import com.deathmotion.antihealthindicator.cache.EntityCache;
+import com.deathmotion.antihealthindicator.cache.entities.CachedEntity;
+import com.deathmotion.antihealthindicator.cache.entities.RidableEntity;
+import com.deathmotion.antihealthindicator.cache.entities.WolfEntity;
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
import com.deathmotion.antihealthindicator.data.RidableEntities;
import com.deathmotion.antihealthindicator.data.Settings;
-import com.deathmotion.antihealthindicator.data.cache.CachedEntity;
-import com.deathmotion.antihealthindicator.data.cache.RidableEntity;
-import com.deathmotion.antihealthindicator.data.cache.WolfEntity;
-import com.deathmotion.antihealthindicator.managers.CacheManager;
import com.deathmotion.antihealthindicator.managers.ConfigManager;
-import com.github.retrooper.packetevents.event.PacketListener;
import com.github.retrooper.packetevents.event.PacketSendEvent;
-import com.github.retrooper.packetevents.event.UserDisconnectEvent;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
@@ -36,36 +35,21 @@
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.wrapper.play.server.*;
-import java.util.UUID;
-
/**
* Listens for EntityState events and manages the caching of various entity state details.
- *
- * @param The platform type.
*/
-public class EntityTracker
implements PacketListener {
- private final CacheManager
cacheManager;
- private final ConfigManager
configManager;
-
- /**
- * Constructs a new EntityState with the specified {@link AHIPlatform}.
- *
- * @param platform The platform to use.
- */
- public EntityTracker(AHIPlatform
platform) {
- this.cacheManager = platform.getCacheManager();
- this.configManager = platform.getConfigManager();
-
- platform.getLogManager().debug("Entity State listener has been set up.");
+public class EntityTracker {
+ private final AHIPlayer player;
+ private final EntityCache entityCache;
+ private final ConfigManager> configManager;
+
+
+ public EntityTracker(AHIPlayer player, EntityCache entityCache) {
+ this.player = player;
+ this.entityCache = entityCache;
+ this.configManager = AHIPlatform.getInstance().getConfigManager();
}
- /**
- * This function is called when an {@link PacketSendEvent} is triggered.
- * Manages the state of various entities based on the event triggered.
- *
- * @param event The event that has been triggered.
- */
- @Override
public void onPacketSend(PacketSendEvent event) {
final Settings settings = configManager.getSettings();
if (!settings.getEntityData().isEnabled()) return;
@@ -73,33 +57,25 @@ public void onPacketSend(PacketSendEvent event) {
final PacketTypeCommon type = event.getPacketType();
if (PacketType.Play.Server.SPAWN_LIVING_ENTITY == type) {
- handleSpawnLivingEntity(new WrapperPlayServerSpawnLivingEntity(event), event.getUser(), settings);
+ handleSpawnLivingEntity(new WrapperPlayServerSpawnLivingEntity(event), settings);
} else if (PacketType.Play.Server.SPAWN_ENTITY == type) {
- handleSpawnEntity(new WrapperPlayServerSpawnEntity(event), event.getUser(), settings);
+ handleSpawnEntity(new WrapperPlayServerSpawnEntity(event), settings);
} else if (PacketType.Play.Server.SPAWN_PLAYER == type) {
- handleSpawnPlayer(new WrapperPlayServerSpawnPlayer(event), event.getUser());
+ handleSpawnPlayer(new WrapperPlayServerSpawnPlayer(event));
} else if (PacketType.Play.Server.ENTITY_METADATA == type) {
handleEntityMetadata(new WrapperPlayServerEntityMetadata(event), event.getUser(), settings);
} else if (PacketType.Play.Server.DESTROY_ENTITIES == type) {
- handleDestroyEntities(new WrapperPlayServerDestroyEntities(event), event.getUser());
+ handleDestroyEntities(new WrapperPlayServerDestroyEntities(event));
} else if (PacketType.Play.Server.RESPAWN == type) {
- handleRespawn(event.getUser());
+ handleRespawn();
} else if (PacketType.Play.Server.JOIN_GAME == type) {
- handleJoinGame(event.getUser());
+ handleJoinGame();
} else if (PacketType.Play.Server.CONFIGURATION_START == type) {
- handleConfigurationStart(event.getUser());
+ handleConfigurationStart();
}
}
- @Override
- public void onUserDisconnect(UserDisconnectEvent event) {
- UUID userUUID = event.getUser().getUUID();
- if (userUUID == null) return;
-
- cacheManager.removeUserCache(userUUID);
- }
-
- private void handleSpawnLivingEntity(WrapperPlayServerSpawnLivingEntity packet, User user, Settings settings) {
+ private void handleSpawnLivingEntity(WrapperPlayServerSpawnLivingEntity packet, Settings settings) {
EntityType entityType = packet.getEntityType();
if (settings.getEntityData().isPlayersOnly()) {
@@ -109,10 +85,10 @@ private void handleSpawnLivingEntity(WrapperPlayServerSpawnLivingEntity packet,
int entityId = packet.getEntityId();
CachedEntity entityData = createLivingEntity(entityType);
- cacheManager.addLivingEntity(user.getUUID(), entityId, entityData);
+ entityCache.addLivingEntity(entityId, entityData);
}
- private void handleSpawnEntity(WrapperPlayServerSpawnEntity packet, User user, Settings settings) {
+ private void handleSpawnEntity(WrapperPlayServerSpawnEntity packet, Settings settings) {
EntityType entityType = packet.getEntityType();
if (EntityTypes.isTypeInstanceOf(entityType, EntityTypes.LIVINGENTITY)) {
@@ -123,15 +99,15 @@ private void handleSpawnEntity(WrapperPlayServerSpawnEntity packet, User user, S
int entityId = packet.getEntityId();
CachedEntity entityData = createLivingEntity(entityType);
- cacheManager.addLivingEntity(user.getUUID(), entityId, entityData);
+ entityCache.addLivingEntity(entityId, entityData);
}
}
- private void handleSpawnPlayer(WrapperPlayServerSpawnPlayer packet, User user) {
+ private void handleSpawnPlayer(WrapperPlayServerSpawnPlayer packet) {
CachedEntity livingEntityData = new CachedEntity();
livingEntityData.setEntityType(EntityTypes.PLAYER);
- cacheManager.addLivingEntity(user.getUUID(), packet.getEntityId(), livingEntityData);
+ entityCache.addLivingEntity(packet.getEntityId(), livingEntityData);
}
private void handleEntityMetadata(WrapperPlayServerEntityMetadata packet, User user, Settings settings) {
@@ -139,28 +115,28 @@ private void handleEntityMetadata(WrapperPlayServerEntityMetadata packet, User u
int entityId = packet.getEntityId();
- CachedEntity entityData = cacheManager.getCachedEntity(user.getUUID(), entityId).orElse(null);
+ CachedEntity entityData = entityCache.getCachedEntity(entityId).orElse(null);
if (entityData == null) return;
- packet.getEntityMetadata().forEach(metaData -> entityData.processMetaData(metaData, user));
+ packet.getEntityMetadata().forEach(metaData -> entityData.processMetaData(metaData, player));
}
- private void handleDestroyEntities(WrapperPlayServerDestroyEntities packet, User user) {
+ private void handleDestroyEntities(WrapperPlayServerDestroyEntities packet) {
for (int entityId : packet.getEntityIds()) {
- cacheManager.removeEntity(user.getUUID(), entityId);
+ entityCache.removeEntity(entityId);
}
}
- private void handleRespawn(User user) {
- cacheManager.resetUserCache(user.getUUID());
+ private void handleRespawn() {
+ entityCache.resetUserCache();
}
- private void handleJoinGame(User user) {
- cacheManager.resetUserCache(user.getUUID());
+ private void handleJoinGame() {
+ entityCache.resetUserCache();
}
- private void handleConfigurationStart(User user) {
- cacheManager.resetUserCache(user.getUUID());
+ private void handleConfigurationStart() {
+ entityCache.resetUserCache();
}
private CachedEntity createLivingEntity(EntityType entityType) {
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/cache/trackers/VehicleTracker.java b/common/src/main/java/com/deathmotion/antihealthindicator/cache/trackers/VehicleTracker.java
new file mode 100644
index 0000000..8f0076c
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/cache/trackers/VehicleTracker.java
@@ -0,0 +1,122 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.cache.trackers;
+
+import com.deathmotion.antihealthindicator.AHIPlatform;
+import com.deathmotion.antihealthindicator.cache.EntityCache;
+import com.deathmotion.antihealthindicator.cache.entities.CachedEntity;
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
+import com.deathmotion.antihealthindicator.data.RidableEntities;
+import com.deathmotion.antihealthindicator.data.Settings;
+import com.deathmotion.antihealthindicator.managers.ConfigManager;
+import com.github.retrooper.packetevents.event.PacketSendEvent;
+import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
+import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
+import com.github.retrooper.packetevents.protocol.packettype.PacketType;
+import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerAttachEntity;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityMetadata;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetPassengers;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Listens for VehicleState events and manages the caching of various entity state details.
+ */
+public class VehicleTracker {
+ private final AHIPlayer player;
+ private final EntityCache entityCache;
+ private final ConfigManager> configManager;
+
+ public VehicleTracker(AHIPlayer player, EntityCache entityCache) {
+ this.player = player;
+ this.entityCache = entityCache;
+ this.configManager = AHIPlatform.getInstance().getConfigManager();
+ }
+
+ public void onPacketSend(PacketSendEvent event) {
+ final Settings settings = configManager.getSettings();
+ if (!settings.getEntityData().isEnabled()) return;
+ if (settings.getEntityData().isPlayersOnly()) return;
+
+ final PacketTypeCommon type = event.getPacketType();
+
+ if (PacketType.Play.Server.SET_PASSENGERS == type) {
+ handlePassengers(new WrapperPlayServerSetPassengers(event));
+ } else if (PacketType.Play.Server.ATTACH_ENTITY == type) {
+ handleAttachEntity(new WrapperPlayServerAttachEntity(event));
+ }
+ }
+
+ private void handlePassengers(WrapperPlayServerSetPassengers packet) {
+ int entityId = packet.getEntityId();
+ if (entityId == player.user.getEntityId() || !isValidVehicle(entityId)) return;
+
+ int[] passengers = packet.getPassengers();
+ if (passengers.length > 0) {
+ updatePassengerState(entityId, passengers[0], true);
+ } else {
+ int passengerId = entityCache.getPassengerId(entityId);
+ updatePassengerState(entityId, passengerId, false);
+ }
+ }
+
+ private void handleAttachEntity(WrapperPlayServerAttachEntity packet) {
+ int entityId = packet.getHoldingId();
+ if (entityId == player.user.getEntityId() || !isValidVehicle(entityId)) return;
+
+ int passengerId = packet.getAttachedId();
+ if (entityId > 0) {
+ updatePassengerState(entityId, passengerId, true);
+ } else {
+ int reversedEntityId = entityCache.getEntityIdByPassengerId(passengerId);
+ updatePassengerState(reversedEntityId, passengerId, false);
+ }
+ }
+
+ private void updatePassengerState(int vehicleId, int passengerId, boolean entering) {
+ entityCache.updateVehiclePassenger(vehicleId, entering ? passengerId : -1);
+ if (entering || player.user.getEntityId() == passengerId) {
+ float healthValue = entering ? entityCache.getVehicleHealth(vehicleId) : 0.5F;
+ sendVehicleHealthUpdate(vehicleId, healthValue);
+ }
+ }
+
+ private boolean isValidVehicle(int entityId) {
+ return entityCache.getCachedEntity(entityId)
+ .map(CachedEntity::getEntityType)
+ .map(RidableEntities::isRideable)
+ .orElse(false);
+ }
+
+ private void sendVehicleHealthUpdate(int vehicleId, float healthValue) {
+ AHIPlatform.getInstance().getScheduler().runAsyncTask((o) -> {
+ List metadata = Collections.singletonList(
+ new EntityData(
+ player.metadataIndex.HEALTH,
+ EntityDataTypes.FLOAT,
+ healthValue
+ )
+ );
+
+ player.user.sendPacketSilently(new WrapperPlayServerEntityMetadata(vehicleId, metadata));
+ });
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/data/AHIPlayer.java b/common/src/main/java/com/deathmotion/antihealthindicator/data/AHIPlayer.java
new file mode 100644
index 0000000..1b6f694
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/data/AHIPlayer.java
@@ -0,0 +1,46 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.data;
+
+import com.deathmotion.antihealthindicator.cache.EntityCache;
+import com.deathmotion.antihealthindicator.managers.SpoofManager;
+import com.deathmotion.antihealthindicator.util.MetadataIndex;
+import com.github.retrooper.packetevents.protocol.player.User;
+
+import java.util.UUID;
+
+public class AHIPlayer {
+ public final UUID uuid;
+ public final User user;
+
+ public final MetadataIndex metadataIndex;
+ public final EntityCache entityCache;
+
+ public final SpoofManager spoofManager;
+
+ public AHIPlayer(User user) {
+ this.uuid = user.getUUID();
+ this.user = user;
+
+ this.metadataIndex = new MetadataIndex(user.getClientVersion());
+ this.entityCache = new EntityCache(this);
+
+ this.spoofManager = new SpoofManager(this);
+ }
+}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/data/Constants.java b/common/src/main/java/com/deathmotion/antihealthindicator/data/Constants.java
index 39f0d9f..8b11ccf 100644
--- a/common/src/main/java/com/deathmotion/antihealthindicator/data/Constants.java
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/data/Constants.java
@@ -21,6 +21,6 @@
public class Constants {
public static final String GITHUB_API_URL = "https://api.github.com/repos/Bram1903/AntiHealthIndicator/releases/latest";
public static final String GITHUB_URL = "https://github.com/Bram1903/AntiHealthIndicator";
- public static final String SPIGOT_URL = "https://www.spigotmc.org/resources/antihealthindicator.114851/";
+ public static final String MODRINTH_URL = "https://modrinth.com/plugin/antihealthindicator";
}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/data/RidableEntities.java b/common/src/main/java/com/deathmotion/antihealthindicator/data/RidableEntities.java
index 7d4993d..2bae56d 100644
--- a/common/src/main/java/com/deathmotion/antihealthindicator/data/RidableEntities.java
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/data/RidableEntities.java
@@ -1,19 +1,19 @@
/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
package com.deathmotion.antihealthindicator.data;
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/data/Settings.java b/common/src/main/java/com/deathmotion/antihealthindicator/data/Settings.java
index 5986ab5..8aac5bb 100644
--- a/common/src/main/java/com/deathmotion/antihealthindicator/data/Settings.java
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/data/Settings.java
@@ -27,7 +27,6 @@ public class Settings {
private boolean Debug = false;
private UpdateChecker UpdateChecker = new UpdateChecker();
- private boolean AllowBypass = false;
private boolean WorldSeed = false;
private boolean FoodSaturation = true;
private boolean TeamScoreboard = true;
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/data/cache/CachedEntity.java b/common/src/main/java/com/deathmotion/antihealthindicator/data/cache/CachedEntity.java
deleted file mode 100644
index 84d95fe..0000000
--- a/common/src/main/java/com/deathmotion/antihealthindicator/data/cache/CachedEntity.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.deathmotion.antihealthindicator.data.cache;
-
-import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
-import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
-import com.github.retrooper.packetevents.protocol.player.User;
-import lombok.Getter;
-import lombok.Setter;
-
-@Getter
-@Setter
-public class CachedEntity {
- private EntityType entityType;
-
- public void processMetaData(EntityData metaData, User user) {
- }
-}
\ No newline at end of file
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/data/cache/RidableEntity.java b/common/src/main/java/com/deathmotion/antihealthindicator/data/cache/RidableEntity.java
deleted file mode 100644
index c3e5b03..0000000
--- a/common/src/main/java/com/deathmotion/antihealthindicator/data/cache/RidableEntity.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.deathmotion.antihealthindicator.data.cache;
-
-import com.deathmotion.antihealthindicator.util.MetadataIndex;
-import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
-import com.github.retrooper.packetevents.protocol.player.User;
-import lombok.Getter;
-import lombok.Setter;
-
-@Getter
-@Setter
-public class RidableEntity extends CachedEntity {
- private float health;
- private int passengerId;
-
- @Override
- public void processMetaData(EntityData metaData, User user) {
- if (metaData.getIndex() == new MetadataIndex(user.getClientVersion()).HEALTH) {
- setHealth((float) metaData.getValue());
- }
- }
-}
\ No newline at end of file
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/data/cache/WolfEntity.java b/common/src/main/java/com/deathmotion/antihealthindicator/data/cache/WolfEntity.java
deleted file mode 100644
index 1221b24..0000000
--- a/common/src/main/java/com/deathmotion/antihealthindicator/data/cache/WolfEntity.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.deathmotion.antihealthindicator.data.cache;
-
-import com.deathmotion.antihealthindicator.util.MetadataIndex;
-import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
-import com.github.retrooper.packetevents.protocol.player.User;
-import lombok.Getter;
-import lombok.Setter;
-
-import java.util.Optional;
-import java.util.UUID;
-
-@Getter
-@Setter
-public class WolfEntity extends CachedEntity {
- private boolean isTamed;
- private UUID ownerUUID;
-
- public boolean isOwnerPresent() {
- return ownerUUID != null;
- }
-
- public UUID getOwnerUUID() {
- if (!isOwnerPresent()) {
- throw new IllegalStateException("Owner UUID not present");
- }
- return ownerUUID;
- }
-
- @Override
- public void processMetaData(EntityData metaData, User user) {
- int index = metaData.getIndex();
- MetadataIndex metadataIndex = new MetadataIndex(user.getClientVersion());
-
- if (index == metadataIndex.TAMABLE_TAMED) {
- setTamed(((Byte) metaData.getValue() & 0x04) != 0);
- } else if (index == metadataIndex.TAMABLE_OWNER) {
- Object value = metaData.getValue();
-
- UUID ownerUUID = value instanceof String
- ? Optional.of((String) value)
- .filter(user.getUUID().toString()::equals)
- .map(UUID::fromString)
- .orElse(null)
- : ((Optional) value)
- .filter(user.getUUID()::equals)
- .orElse(null);
-
- setOwnerUUID(ownerUUID);
- }
- }
-}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/managers/CacheManager.java b/common/src/main/java/com/deathmotion/antihealthindicator/managers/CacheManager.java
deleted file mode 100644
index ec4c3db..0000000
--- a/common/src/main/java/com/deathmotion/antihealthindicator/managers/CacheManager.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.deathmotion.antihealthindicator.managers;
-
-import com.deathmotion.antihealthindicator.AHIPlatform;
-import com.deathmotion.antihealthindicator.data.cache.CachedEntity;
-import com.deathmotion.antihealthindicator.data.cache.RidableEntity;
-import lombok.Getter;
-import lombok.NonNull;
-import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.format.NamedTextColor;
-import net.kyori.adventure.text.format.TextDecoration;
-
-import java.util.Map;
-import java.util.Optional;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Manages the cache services of the platform.
- *
- * @param The platform type.
- */
-@Getter
-public class CacheManager
{
- private final ConcurrentHashMap> cache;
-
- private final AHIPlatform platform;
- private final ConfigManager
configManager;
- private final LogManager
logManager;
-
- public CacheManager(AHIPlatform
platform) {
- this.cache = new ConcurrentHashMap<>();
-
- this.platform = platform;
- this.logManager = platform.getLogManager();
- this.configManager = platform.getConfigManager();
-
- LogCacheStats();
-
- this.platform.getLogManager().debug("CacheManager initialized.");
- }
-
- public Map getUserCache(@NonNull UUID uuid) {
- return this.cache.computeIfAbsent(uuid, u -> new ConcurrentHashMap<>());
- }
-
- public void removeUserCache(@NonNull UUID uuid) {
- this.cache.remove(uuid);
- }
-
- public Optional getCachedEntity(@NonNull UUID uuid, int entityId) {
- return Optional.ofNullable(getUserCache(uuid).get(entityId));
- }
-
- public Optional getVehicleData(@NonNull UUID uuid, int entityId) {
- return getCachedEntity(uuid, entityId)
- .filter(entityData -> entityData instanceof RidableEntity)
- .map(entityData -> (RidableEntity) entityData);
- }
-
- public void addLivingEntity(@NonNull UUID uuid, int entityId, @NonNull CachedEntity cachedEntity) {
- getUserCache(uuid).put(entityId, cachedEntity);
- }
-
- public void removeEntity(@NonNull UUID uuid, int entityId) {
- getUserCache(uuid).remove(entityId);
- }
-
- public void resetUserCache(@NonNull UUID uuid) {
- getUserCache(uuid).clear();
- }
-
- public void updateVehiclePassenger(@NonNull UUID uuid, int entityId, int passengerId) {
- getVehicleData(uuid, entityId).ifPresent(ridableEntityData -> ridableEntityData.setPassengerId(passengerId));
- }
-
- public float getVehicleHealth(@NonNull UUID uuid, int entityId) {
- return getVehicleData(uuid, entityId).map(RidableEntity::getHealth).orElse(0.5f);
- }
-
- public boolean isUserPassenger(@NonNull UUID uuid, int entityId, int userId) {
- return getVehicleData(uuid, entityId).map(ridableEntityData -> ridableEntityData.getPassengerId() == userId).orElse(false);
- }
-
- public int getPassengerId(@NonNull UUID uuid, int entityId) {
- return getVehicleData(uuid, entityId).map(RidableEntity::getPassengerId).orElse(0);
- }
-
- public int getEntityIdByPassengerId(@NonNull UUID uuid, int passengerId) {
- return getUserCache(uuid).entrySet().stream()
- .filter(entry -> entry.getValue() instanceof RidableEntity
- && ((RidableEntity) entry.getValue()).getPassengerId() == passengerId)
- .map(Map.Entry::getKey)
- .findFirst()
- .orElse(0);
- }
-
- private void LogCacheStats() {
- platform.getScheduler().runAsyncTaskAtFixedRate((o) -> {
- if (!configManager.getSettings().isDebug()) return;
-
- ConcurrentHashMap> cacheMap = cache;
-
- int underlyingSize = cacheMap.values().stream().mapToInt(Map::size).sum();
- int avgCacheSizePerUser = cacheMap.isEmpty() ? 0 : Math.floorDiv(underlyingSize, cacheMap.size());
-
- Component statsComponent = Component.text()
- .append(Component.text("[DEBUG] Cache Stats", NamedTextColor.GREEN)
- .decoration(TextDecoration.BOLD, true))
- .appendNewline()
- .append(Component.text("\n\u25cf User Cache Size: ", NamedTextColor.GREEN)
- .decoration(TextDecoration.BOLD, true))
- .append(Component.text(cache.size(), NamedTextColor.AQUA))
- .append(Component.text("\n\u25cf Average Cache Size Per User: ", NamedTextColor.GREEN)
- .decoration(TextDecoration.BOLD, true))
- .append(Component.text(avgCacheSizePerUser, NamedTextColor.AQUA))
- .append(Component.text("\n\u25cf Underlying Cache Size: ", NamedTextColor.GREEN)
- .decoration(TextDecoration.BOLD, true))
- .append(Component.text(underlyingSize, NamedTextColor.AQUA))
- .build();
-
- platform.broadcastComponent(statsComponent, "AntiHealthIndicator.Debug");
- }, 10, 10, TimeUnit.SECONDS);
- }
-}
\ No newline at end of file
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/managers/ConfigManager.java b/common/src/main/java/com/deathmotion/antihealthindicator/managers/ConfigManager.java
index 20af134..345a0ac 100644
--- a/common/src/main/java/com/deathmotion/antihealthindicator/managers/ConfigManager.java
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/managers/ConfigManager.java
@@ -92,7 +92,6 @@ private void setConfigOptions(Map yamlData, Settings settings) {
settings.getUpdateChecker().setEnabled(getBoolean(yamlData, "update-checker.enabled", true));
settings.getUpdateChecker().setPrintToConsole(getBoolean(yamlData, "update-checker.print-to-console", true));
settings.getUpdateChecker().setNotifyInGame(getBoolean(yamlData, "update-checker.notify-in-game", true));
- settings.setAllowBypass(getBoolean(yamlData, "allow-bypass.enabled", false));
settings.setWorldSeed(getBoolean(yamlData, "spoof.world-seed.enabled", false));
settings.setFoodSaturation(getBoolean(yamlData, "spoof.food-saturation.enabled", true));
settings.setTeamScoreboard(getBoolean(yamlData, "spoof.team-scoreboard.enabled", true));
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/managers/PacketManager.java b/common/src/main/java/com/deathmotion/antihealthindicator/managers/PacketManager.java
deleted file mode 100644
index 1accdaa..0000000
--- a/common/src/main/java/com/deathmotion/antihealthindicator/managers/PacketManager.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.deathmotion.antihealthindicator.managers;
-
-import com.deathmotion.antihealthindicator.AHIPlatform;
-import com.deathmotion.antihealthindicator.packetlisteners.EntityTracker;
-import com.deathmotion.antihealthindicator.packetlisteners.VehicleState;
-import com.deathmotion.antihealthindicator.packetlisteners.spoofers.*;
-import com.github.retrooper.packetevents.PacketEvents;
-import com.github.retrooper.packetevents.event.PacketListenerPriority;
-
-/**
- * Manager for handling packet listeners
- *
- * @param The platform type
- */
-public class PacketManager
{
- private final AHIPlatform
platform;
-
- /**
- * Constructs a new PacketManager with the specified {@link AHIPlatform}.
- *
- * @param platform The platform to use.
- */
- public PacketManager(AHIPlatform
platform) {
- this.platform = platform;
-
- setupPacketListeners();
- platform.getLogManager().debug("Packet listeners have been set up.");
- }
-
- /**
- * Sets up packet listeners
- */
- public void setupPacketListeners() {
- PacketEvents.getAPI().getEventManager().registerListener(new EntityTracker<>(platform), PacketListenerPriority.LOW);
- PacketEvents.getAPI().getEventManager().registerListener(new EntityMetadataListener<>(platform));
- PacketEvents.getAPI().getEventManager().registerListener(new VehicleState<>(platform));
- PacketEvents.getAPI().getEventManager().registerListener(new EntityEquipmentListener<>(platform));
- PacketEvents.getAPI().getEventManager().registerListener(new PlayerUpdateHealthListener<>(platform));
- PacketEvents.getAPI().getEventManager().registerListener(new WorldSeedListener<>(platform));
- PacketEvents.getAPI().getEventManager().registerListener(new ScoreboardListener<>(platform));
- }
-}
\ No newline at end of file
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/managers/PlayerDataManager.java b/common/src/main/java/com/deathmotion/antihealthindicator/managers/PlayerDataManager.java
new file mode 100644
index 0000000..756dd3a
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/managers/PlayerDataManager.java
@@ -0,0 +1,59 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.managers;
+
+import com.deathmotion.antihealthindicator.AHIPlatform;
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
+import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
+import com.github.retrooper.packetevents.protocol.player.User;
+
+import javax.annotation.Nullable;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class PlayerDataManager
{
+
+ private final AHIPlatform
platform;
+ private final ConcurrentHashMap playerDataMap = new ConcurrentHashMap<>();
+
+ public PlayerDataManager(AHIPlatform platform) {
+ this.platform = platform;
+ }
+
+ public boolean shouldCheck(User user) {
+ if (!ChannelHelper.isOpen(user.getChannel())) return false;
+ if (user.getUUID() == null) return false;
+
+ return !platform.hasPermission(user.getUUID(), "AntiHealthIndicator.Bypass");
+ }
+
+ @Nullable
+ public AHIPlayer getPlayer(final User user) {
+ return playerDataMap.get(user);
+ }
+
+ public void addUser(final User user) {
+ if (shouldCheck(user)) {
+ playerDataMap.put(user, new AHIPlayer(user));
+ }
+ }
+
+ public void remove(final User player) {
+ playerDataMap.remove(player);
+ }
+}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/managers/SpoofManager.java b/common/src/main/java/com/deathmotion/antihealthindicator/managers/SpoofManager.java
new file mode 100644
index 0000000..d4de60f
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/managers/SpoofManager.java
@@ -0,0 +1,47 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.managers;
+
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
+import com.deathmotion.antihealthindicator.spoofers.impl.*;
+import com.deathmotion.antihealthindicator.spoofers.type.PacketSpoofer;
+import com.github.retrooper.packetevents.event.PacketSendEvent;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+
+public class SpoofManager {
+
+ ClassToInstanceMap spoofers;
+
+ public SpoofManager(AHIPlayer player) {
+ spoofers = new ImmutableClassToInstanceMap.Builder()
+ .put(MetadataSpoofer.class, new MetadataSpoofer(player))
+ .put(EquipmentSpoofer.class, new EquipmentSpoofer(player))
+ .put(ScoreboardSpoofer.class, new ScoreboardSpoofer(player))
+ .put(FoodSaturationSpoofer.class, new FoodSaturationSpoofer(player))
+ .put(WorldSeedSpoofer.class, new WorldSeedSpoofer(player))
+ .build();
+ }
+
+ public void onPacketSend(final PacketSendEvent packet) {
+ for (PacketSpoofer packetSpoofer : spoofers.values()) {
+ packetSpoofer.onPacketSend(packet);
+ }
+ }
+}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/UpdateNotifier.java b/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/UpdateNotifier.java
deleted file mode 100644
index a944566..0000000
--- a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/UpdateNotifier.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.deathmotion.antihealthindicator.packetlisteners;
-
-import com.deathmotion.antihealthindicator.AHIPlatform;
-import com.deathmotion.antihealthindicator.api.versioning.AHIVersion;
-import com.deathmotion.antihealthindicator.data.Constants;
-import com.deathmotion.antihealthindicator.data.Settings;
-import com.github.retrooper.packetevents.event.PacketListenerAbstract;
-import com.github.retrooper.packetevents.event.UserLoginEvent;
-import com.github.retrooper.packetevents.protocol.player.User;
-import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.event.ClickEvent;
-import net.kyori.adventure.text.event.HoverEvent;
-import net.kyori.adventure.text.format.NamedTextColor;
-import net.kyori.adventure.text.format.TextDecoration;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Listens for PlayerJoin events and informs the user about the latest version of the application.
- *
- * @param The platform type.
- */
-public class UpdateNotifier
extends PacketListenerAbstract {
- private final AHIPlatform
platform;
- private final Component updateComponent;
-
- /**
- * Constructs a new PlayerJoin with the specified {@link AHIPlatform} and latestVersion of the application.
- *
- * @param platform The platform to use.
- * @param latestVersion The latest version of the application.
- */
- public UpdateNotifier(AHIPlatform
platform, AHIVersion latestVersion) {
- this.platform = platform;
-
- this.updateComponent = Component.text()
- .append(Component.text("[AntiHealthIndicator] ", NamedTextColor.RED)
- .decoration(TextDecoration.BOLD, true))
- .append(Component.text("Version " + latestVersion.toStringWithoutSnapshot() + " is ", NamedTextColor.GREEN))
- .append(Component.text("now available", NamedTextColor.GREEN)
- .decorate(TextDecoration.UNDERLINED)
- .hoverEvent(HoverEvent.showText(Component.text("Click to download", NamedTextColor.GREEN)))
- .clickEvent(ClickEvent.openUrl(Constants.SPIGOT_URL)))
- .append(Component.text("!", NamedTextColor.GREEN))
- .build();
-
- platform.getLogManager().debug("Update detected. Player join listener has been set up.");
- }
-
- /**
- * This function is called when an {@link UserLoginEvent} is triggered.
- * Sends a message to the player about the latest version of the application.
- *
- * @param event The event that has been triggered.
- */
- @Override
- public void onUserLogin(UserLoginEvent event) {
- final Settings settings = platform.getConfigManager().getSettings();
- if (!settings.getUpdateChecker().isNotifyInGame()) return;
-
- User user = event.getUser();
-
- platform.getScheduler().runAsyncTaskDelayed((o) -> {
- if (platform.hasPermission(user.getUUID(), "AntiHealthIndicator.Notify")) {
- user.sendMessage(updateComponent);
- }
- }, 2, TimeUnit.SECONDS);
- }
-}
\ No newline at end of file
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/VehicleState.java b/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/VehicleState.java
deleted file mode 100644
index 656a7ef..0000000
--- a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/VehicleState.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.deathmotion.antihealthindicator.packetlisteners;
-
-import com.deathmotion.antihealthindicator.AHIPlatform;
-import com.deathmotion.antihealthindicator.data.RidableEntities;
-import com.deathmotion.antihealthindicator.data.Settings;
-import com.deathmotion.antihealthindicator.data.cache.CachedEntity;
-import com.deathmotion.antihealthindicator.managers.CacheManager;
-import com.deathmotion.antihealthindicator.managers.ConfigManager;
-import com.deathmotion.antihealthindicator.util.MetadataIndex;
-import com.github.retrooper.packetevents.event.PacketListenerAbstract;
-import com.github.retrooper.packetevents.event.PacketSendEvent;
-import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
-import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
-import com.github.retrooper.packetevents.protocol.packettype.PacketType;
-import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
-import com.github.retrooper.packetevents.protocol.player.User;
-import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerAttachEntity;
-import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityMetadata;
-import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetPassengers;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Listens for VehicleState events and manages the caching of various entity state details.
- *
- * @param
The platform type.
- */
-public class VehicleState
extends PacketListenerAbstract {
- private final AHIPlatform
platform;
- private final ConfigManager
configManager;
- private final CacheManager
cacheManager;
-
- public VehicleState(AHIPlatform
platform) {
- this.platform = platform;
- this.configManager = platform.getConfigManager();
- this.cacheManager = platform.getCacheManager();
-
- platform.getLogManager().debug("Vehicle State listener has been set up.");
- }
-
- @Override
- public void onPacketSend(PacketSendEvent event) {
- final Settings settings = configManager.getSettings();
- if (!settings.getEntityData().isEnabled()) return;
- if (settings.getEntityData().isPlayersOnly()) return;
-
- final PacketTypeCommon type = event.getPacketType();
-
- if (PacketType.Play.Server.SET_PASSENGERS == type) {
- handlePassengers(new WrapperPlayServerSetPassengers(event), event.getUser(), settings);
- } else if (PacketType.Play.Server.ATTACH_ENTITY == type) {
- handleAttachEntity(new WrapperPlayServerAttachEntity(event), event.getUser(), settings);
- }
- }
-
- private void handlePassengers(WrapperPlayServerSetPassengers packet, User user, Settings settings) {
- int entityId = packet.getEntityId();
- if (entityId == user.getEntityId() || !isValidVehicle(user.getUUID(), entityId)) return;
-
- int[] passengers = packet.getPassengers();
- if (passengers.length > 0) {
- updatePassengerState(user, entityId, passengers[0], true, settings);
- } else {
- int passengerId = cacheManager.getPassengerId(user.getUUID(), entityId);
- updatePassengerState(user, entityId, passengerId, false, settings);
- }
- }
-
- private void handleAttachEntity(WrapperPlayServerAttachEntity packet, User user, Settings settings) {
- int entityId = packet.getHoldingId();
- if (entityId == user.getEntityId() || !isValidVehicle(user.getUUID(), entityId)) return;
-
- int passengerId = packet.getAttachedId();
- if (entityId > 0) {
- updatePassengerState(user, entityId, passengerId, true, settings);
- } else {
- int reversedEntityId = cacheManager.getEntityIdByPassengerId(user.getUUID(), passengerId);
- updatePassengerState(user, reversedEntityId, passengerId, false, settings);
- }
- }
-
- private void updatePassengerState(User user, int vehicleId, int passengerId, boolean entering, Settings settings) {
- cacheManager.updateVehiclePassenger(user.getUUID(), vehicleId, entering ? passengerId : -1);
- if (entering || user.getEntityId() == passengerId) {
- float healthValue = entering ? cacheManager.getVehicleHealth(user.getUUID(), vehicleId) : 0.5F;
- sendVehicleHealthUpdate(user, vehicleId, healthValue, entering, settings);
- }
- }
-
- private boolean isValidVehicle(UUID userUUID, int entityId) {
- return cacheManager.getCachedEntity(userUUID, entityId)
- .map(CachedEntity::getEntityType)
- .map(RidableEntities::isRideable)
- .orElse(false);
- }
-
- private void sendVehicleHealthUpdate(User user, int vehicleId, float healthValue, boolean entering, Settings settings) {
- platform.getScheduler().runAsyncTask((o) -> {
- if (!entering && settings.isAllowBypass() && platform.hasPermission(user.getUUID(), "AntiHealthIndicator.Bypass")) {
- return;
- }
-
- List metadata = Collections.singletonList(
- new EntityData(
- new MetadataIndex(user.getClientVersion()).HEALTH,
- EntityDataTypes.FLOAT,
- healthValue
- )
- );
-
- user.sendPacketSilently(new WrapperPlayServerEntityMetadata(vehicleId, metadata));
- });
- }
-}
\ No newline at end of file
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/EntityMetadataListener.java b/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/EntityMetadataListener.java
deleted file mode 100644
index be7bae1..0000000
--- a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/EntityMetadataListener.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.deathmotion.antihealthindicator.packetlisteners.spoofers;
-
-import com.deathmotion.antihealthindicator.AHIPlatform;
-import com.deathmotion.antihealthindicator.data.Settings;
-import com.deathmotion.antihealthindicator.data.cache.CachedEntity;
-import com.deathmotion.antihealthindicator.data.cache.WolfEntity;
-import com.deathmotion.antihealthindicator.managers.CacheManager;
-import com.deathmotion.antihealthindicator.managers.ConfigManager;
-import com.deathmotion.antihealthindicator.util.MetadataIndex;
-import com.github.retrooper.packetevents.PacketEvents;
-import com.github.retrooper.packetevents.event.PacketListenerAbstract;
-import com.github.retrooper.packetevents.event.PacketSendEvent;
-import com.github.retrooper.packetevents.manager.server.ServerVersion;
-import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
-import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
-import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
-import com.github.retrooper.packetevents.protocol.packettype.PacketType;
-import com.github.retrooper.packetevents.protocol.player.User;
-import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityMetadata;
-
-/**
- * Listens for EntityMetadata packets and modifies certain entity attributes
- * based on configuration settings.
- *
- * @param The platform type.
- */
-public class EntityMetadataListener
extends PacketListenerAbstract {
- private final AHIPlatform
platform;
- private final ConfigManager
configManager;
- private final CacheManager
cacheManager;
- private final boolean healthTexturesSupported;
-
- public EntityMetadataListener(AHIPlatform
platform) {
- this.platform = platform;
- this.configManager = platform.getConfigManager();
- this.cacheManager = platform.getCacheManager();
-
- // Health textures are supported for server versions 1.15 and above.
- this.healthTexturesSupported = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_15);
-
- platform.getLogManager().debug("Entity Metadata listener initialized.");
- }
-
- @Override
- public void onPacketSend(PacketSendEvent event) {
- if (!event.getPacketType().equals(PacketType.Play.Server.ENTITY_METADATA)) return;
-
- Settings settings = configManager.getSettings();
- if (!settings.getEntityData().isEnabled()) return;
-
- WrapperPlayServerEntityMetadata packet = new WrapperPlayServerEntityMetadata(event);
- int entityId = packet.getEntityId();
- User user = event.getUser();
-
- // Do not process if the packet refers to the user’s own entity or if the user has bypass permissions.
- if (entityId == user.getEntityId() || shouldBypass(user, settings)) return;
-
- CachedEntity cachedEntity = cacheManager.getCachedEntity(user.getUUID(), entityId).orElse(null);
- if (cachedEntity == null) return;
-
- EntityType entityType = cachedEntity.getEntityType();
- if (shouldIgnoreEntity(entityType, user, entityId, cachedEntity, settings)) return;
-
- MetadataIndex metadataIndex = new MetadataIndex(user.getClientVersion());
- packet.getEntityMetadata().forEach(entityData -> handleEntityMetadata(entityType, entityData, metadataIndex, settings));
- event.markForReEncode(true);
- }
-
- private boolean shouldBypass(User user, Settings settings) {
- return settings.isAllowBypass() && platform.hasPermission(user.getUUID(), "AntiHealthIndicator.Bypass");
- }
-
- private boolean shouldIgnoreEntity(EntityType entityType, User user, int entityId, CachedEntity cachedEntity, Settings settings) {
- // Ignore entities with a boss bar (that shows the health already anyway)
- if (entityType == EntityTypes.WITHER || entityType == EntityTypes.ENDER_DRAGON) {
- return true;
- }
-
- // If only players should be processed, skip non-player entities.
- if (settings.getEntityData().isPlayersOnly() && entityType != EntityTypes.PLAYER) {
- return true;
- }
-
- // Optionally ignore vehicles.
- if (!settings.getEntityData().isPlayersOnly() && settings.getEntityData().isIgnoreVehicles() && cacheManager.isUserPassenger(user.getUUID(), entityId, user.getEntityId())) {
- return true;
- }
-
- // Special handling for wolves.
- return entityType == EntityTypes.WOLF && settings.getEntityData().getWolves().isEnabled() && shouldIgnoreWolf(user, cachedEntity, settings);
- }
-
- private boolean shouldIgnoreWolf(User user, CachedEntity cachedEntity, Settings settings) {
- WolfEntity wolfEntity = (WolfEntity) cachedEntity;
- boolean ignoreBasedOnSettings = !settings.getEntityData().getWolves().isTamed() && !settings.getEntityData().getWolves().isOwner();
- boolean isTamed = settings.getEntityData().getWolves().isTamed() && wolfEntity.isTamed();
- boolean isOwnedByUser = settings.getEntityData().getWolves().isOwner() && wolfEntity.isOwnerPresent() && wolfEntity.getOwnerUUID().equals(user.getUUID());
- return ignoreBasedOnSettings || isTamed || isOwnedByUser;
- }
-
- /**
- * Modifies the metadata for the given entity based on its type and settings.
- */
- private void handleEntityMetadata(EntityType entityType, EntityData entityData, MetadataIndex metadataIndex, Settings settings) {
- if (entityType == EntityTypes.IRON_GOLEM && settings.getEntityData().getIronGolems().isEnabled()) {
- if (!settings.getEntityData().getIronGolems().isGradual() || !healthTexturesSupported) {
- applyDefaultSpoofing(entityData, metadataIndex, settings);
- } else {
- spoofIronGolemMetadata(entityData, metadataIndex, settings);
- }
- } else {
- applyDefaultSpoofing(entityData, metadataIndex, settings);
- if (entityType == EntityTypes.PLAYER) {
- spoofPlayerMetadata(entityData, metadataIndex, settings);
- }
- }
- }
-
- /**
- * Applies default spoofing logic for common entity metadata.
- */
- private void applyDefaultSpoofing(EntityData entityData, MetadataIndex metadataIndex, Settings settings) {
- updateAirTicks(entityData, metadataIndex, settings);
- if (entityData.getIndex() == metadataIndex.HEALTH && settings.getEntityData().isHealth()) {
- float health = (Float) entityData.getValue();
- if (health > 0) {
- entityData.setValue(0.5f);
- }
- }
- }
-
- /**
- * Modifies the metadata for iron golems gradually.
- */
- private void spoofIronGolemMetadata(EntityData entityData, MetadataIndex metadataIndex, Settings settings) {
- updateAirTicks(entityData, metadataIndex, settings);
- if (entityData.getIndex() == metadataIndex.HEALTH && settings.getEntityData().isHealth()) {
- float health = (Float) entityData.getValue();
- if (health > 74f) {
- entityData.setValue(100f);
- } else if (health > 49f) {
- entityData.setValue(74f);
- } else if (health > 24f) {
- entityData.setValue(49f);
- } else {
- entityData.setValue(24f);
- }
- }
- }
-
- /**
- * Modifies the metadata for player entities.
- */
- private void spoofPlayerMetadata(EntityData entityData, MetadataIndex metadataIndex, Settings settings) {
- if (entityData.getIndex() == metadataIndex.ABSORPTION && settings.getEntityData().isAbsorption()) {
- setDynamicValue(entityData, 0);
- }
- if (entityData.getIndex() == metadataIndex.XP && settings.getEntityData().isXp()) {
- setDynamicValue(entityData, 0);
- }
- }
-
- /**
- * Updates the air ticks metadata if enabled.
- */
- private void updateAirTicks(EntityData entityData, MetadataIndex metadataIndex, Settings settings) {
- if (entityData.getIndex() == metadataIndex.AIR_TICKS && settings.getEntityData().isAirTicks()) {
- setDynamicValue(entityData, 1);
- }
- }
-
- /**
- * Sets a new value for the entity data while preserving its original numeric type.
- *
- * This method is necessary because the metadata value is stored as an {@code Object}
- * and can be of different numeric types (e.g., Integer, Short, Byte, Long, Float, or Double).
- *
- * @param entityData The metadata object to modify.
- * @param spoofValue The new value to set.
- */
- private void setDynamicValue(EntityData entityData, int spoofValue) {
- Object value = entityData.getValue();
-
- if (value instanceof Integer) {
- entityData.setValue(spoofValue);
- } else if (value instanceof Short) {
- entityData.setValue((short) spoofValue);
- } else if (value instanceof Byte) {
- entityData.setValue((byte) spoofValue);
- } else if (value instanceof Long) {
- entityData.setValue((long) spoofValue);
- } else if (value instanceof Float) {
- entityData.setValue((float) spoofValue);
- } else if (value instanceof Double) {
- entityData.setValue((double) spoofValue);
- }
- }
-}
\ No newline at end of file
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/PlayerUpdateHealthListener.java b/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/PlayerUpdateHealthListener.java
deleted file mode 100644
index 9115bd4..0000000
--- a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/PlayerUpdateHealthListener.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.deathmotion.antihealthindicator.packetlisteners.spoofers;
-
-import com.deathmotion.antihealthindicator.AHIPlatform;
-import com.deathmotion.antihealthindicator.data.Settings;
-import com.deathmotion.antihealthindicator.managers.ConfigManager;
-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.WrapperPlayServerUpdateHealth;
-
-/**
- * Listens for PlayerUpdateHealth events to modify the health display.
- *
- * @param
The platform type.
- */
-public class PlayerUpdateHealthListener
extends PacketListenerAbstract {
- private final AHIPlatform
platform;
- private final ConfigManager
configManager;
-
- /**
- * Constructs a new PlayerUpdateHealthListener with the specified {@link AHIPlatform}.
- *
- * @param platform The platform to use.
- */
- public PlayerUpdateHealthListener(AHIPlatform
platform) {
- this.platform = platform;
- this.configManager = platform.getConfigManager();
-
- platform.getLogManager().debug("Player Update Health listener has been set up.");
- }
-
- /**
- * This function is called when an {@link PacketSendEvent} is triggered.
- * Overwrites the {@link WrapperPlayServerUpdateHealth} for players to control how they are displayed.
- *
- * @param event The event that has been triggered.
- */
- @Override
- public void onPacketSend(PacketSendEvent event) {
- if (event.getPacketType() != PacketType.Play.Server.UPDATE_HEALTH) return;
-
- final Settings settings = configManager.getSettings();
- if (!settings.isFoodSaturation()) return;
-
- if (settings.isAllowBypass()) {
- if (platform.hasPermission(event.getUser().getUUID(), "AntiHealthIndicator.Bypass")) return;
- }
-
- WrapperPlayServerUpdateHealth packet = new WrapperPlayServerUpdateHealth(event);
-
- if (packet.getFoodSaturation() > 0) {
- packet.setFoodSaturation(Float.NaN);
- event.markForReEncode(true);
- }
- }
-}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/WorldSeedListener.java b/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/WorldSeedListener.java
deleted file mode 100644
index aa82e91..0000000
--- a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/WorldSeedListener.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.deathmotion.antihealthindicator.packetlisteners.spoofers;
-
-import com.deathmotion.antihealthindicator.AHIPlatform;
-import com.deathmotion.antihealthindicator.data.Settings;
-import com.deathmotion.antihealthindicator.managers.ConfigManager;
-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.WrapperPlayServerJoinGame;
-import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRespawn;
-
-/**
- * Listens for WorldSeed events to modify the seed value.
- *
- * @param
The platform type.
- */
-public class WorldSeedListener
extends PacketListenerAbstract {
-
- private final AHIPlatform
platform;
- private final ConfigManager
settings;
-
- /**
- * Constructs a new WorldSeedListener with the specified {@link AHIPlatform}.
- *
- * @param platform The platform to use.
- */
- public WorldSeedListener(AHIPlatform
platform) {
- this.platform = platform;
- this.settings = platform.getConfigManager();
-
- platform.getLogManager().debug("World Seed listener has been set up.");
- }
-
- /**
- * This function is called when an {@link PacketSendEvent} is triggered.
- * Overwrites the {@link WrapperPlayServerJoinGame} and {@link WrapperPlayServerRespawn} packets
- * to control seed value.
- *
- * @param event The event that has been triggered.
- */
- @Override
- public void onPacketSend(PacketSendEvent event) {
- final Settings settings = this.settings.getSettings();
- if (!settings.isWorldSeed()) return;
-
- if (event.getPacketType().equals(PacketType.Play.Server.JOIN_GAME)) {
- if (settings.isAllowBypass()) {
- if (platform.hasPermission(event.getUser().getUUID(), "AntiHealthIndicator.Bypass")) return;
- }
-
- WrapperPlayServerJoinGame wrapper = new WrapperPlayServerJoinGame(event);
- wrapper.setHashedSeed(0L);
- event.markForReEncode(true);
- }
- if (event.getPacketType().equals(PacketType.Play.Server.RESPAWN)) {
- if (settings.isAllowBypass()) {
- if (platform.hasPermission(event.getUser().getUUID(), "AntiHealthIndicator.Bypass")) return;
- }
-
- WrapperPlayServerRespawn wrapper = new WrapperPlayServerRespawn(event);
- wrapper.setHashedSeed(0L);
- event.markForReEncode(true);
- }
- }
-}
\ No newline at end of file
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/packets/PacketPlayerJoinQuit.java b/common/src/main/java/com/deathmotion/antihealthindicator/packets/PacketPlayerJoinQuit.java
new file mode 100644
index 0000000..341808b
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/packets/PacketPlayerJoinQuit.java
@@ -0,0 +1,62 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.packets;
+
+import com.deathmotion.antihealthindicator.AHIPlatform;
+import com.github.retrooper.packetevents.event.PacketListenerAbstract;
+import com.github.retrooper.packetevents.event.PacketSendEvent;
+import com.github.retrooper.packetevents.event.UserDisconnectEvent;
+import com.github.retrooper.packetevents.event.UserLoginEvent;
+import com.github.retrooper.packetevents.protocol.packettype.PacketType;
+import com.github.retrooper.packetevents.protocol.player.User;
+
+import java.util.concurrent.TimeUnit;
+
+public class PacketPlayerJoinQuit
extends PacketListenerAbstract {
+
+ private final AHIPlatform
platform;
+
+ public PacketPlayerJoinQuit(AHIPlatform
platform) {
+ this.platform = platform;
+ }
+
+ @Override
+ public void onPacketSend(PacketSendEvent event) {
+ if (event.getPacketType() == PacketType.Login.Server.LOGIN_SUCCESS) {
+ // Do this after send to avoid sending packets before the PLAY state
+ event.getTasksAfterSend().add(() -> platform.getPlayerDataManager().addUser(event.getUser()));
+ }
+ }
+
+ @Override
+ public void onUserLogin(UserLoginEvent event) {
+ if (platform.getConfigManager().getSettings().getUpdateChecker().isNotifyInGame() && platform.getUpdateChecker().isUpdateAvailable()) {
+ User user = event.getUser();
+
+ if (platform.hasPermission(user.getUUID(), "AntiHealthIndicator.Update")) {
+ platform.getScheduler().runAsyncTaskDelayed((o) -> user.sendMessage(platform.getUpdateChecker().getUpdateComponent()), 2, TimeUnit.SECONDS);
+ }
+ }
+ }
+
+ @Override
+ public void onUserDisconnect(UserDisconnectEvent event) {
+ platform.getPlayerDataManager().remove(event.getUser());
+ }
+}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/packets/SpoofManagerPacketListener.java b/common/src/main/java/com/deathmotion/antihealthindicator/packets/SpoofManagerPacketListener.java
new file mode 100644
index 0000000..61e6fa1
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/packets/SpoofManagerPacketListener.java
@@ -0,0 +1,46 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.packets;
+
+import com.deathmotion.antihealthindicator.AHIPlatform;
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
+import com.github.retrooper.packetevents.event.PacketListenerAbstract;
+import com.github.retrooper.packetevents.event.PacketListenerPriority;
+import com.github.retrooper.packetevents.event.PacketSendEvent;
+import com.github.retrooper.packetevents.protocol.ConnectionState;
+
+public class SpoofManagerPacketListener
extends PacketListenerAbstract {
+
+ private final AHIPlatform
platform;
+
+ public SpoofManagerPacketListener(AHIPlatform
platform) {
+ super(PacketListenerPriority.LOW);
+ this.platform = platform;
+ }
+
+ @Override
+ public void onPacketSend(final PacketSendEvent event) {
+ if (event.getConnectionState() != ConnectionState.PLAY) return;
+ AHIPlayer player = platform.getPlayerDataManager().getPlayer(event.getUser());
+ if (player == null) return;
+
+ player.entityCache.onPacketSend(event);
+ player.spoofManager.onPacketSend(event);
+ }
+}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/Spoofer.java b/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/Spoofer.java
new file mode 100644
index 0000000..e29efa4
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/Spoofer.java
@@ -0,0 +1,34 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.spoofers;
+
+import com.deathmotion.antihealthindicator.AHIPlatform;
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
+import com.deathmotion.antihealthindicator.managers.ConfigManager;
+
+public class Spoofer {
+
+ protected final AHIPlayer player;
+ protected final ConfigManager> configManager;
+
+ public Spoofer(AHIPlayer player) {
+ this.player = player;
+ this.configManager = AHIPlatform.getInstance().getConfigManager();
+ }
+}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/EntityEquipmentListener.java b/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/EquipmentSpoofer.java
similarity index 70%
rename from common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/EntityEquipmentListener.java
rename to common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/EquipmentSpoofer.java
index 9473316..86738e1 100644
--- a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/EntityEquipmentListener.java
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/EquipmentSpoofer.java
@@ -1,28 +1,28 @@
/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
-package com.deathmotion.antihealthindicator.packetlisteners.spoofers;
+package com.deathmotion.antihealthindicator.spoofers.impl;
-import com.deathmotion.antihealthindicator.AHIPlatform;
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
import com.deathmotion.antihealthindicator.data.Settings;
-import com.deathmotion.antihealthindicator.managers.ConfigManager;
+import com.deathmotion.antihealthindicator.spoofers.Spoofer;
+import com.deathmotion.antihealthindicator.spoofers.type.PacketSpoofer;
import com.github.retrooper.packetevents.PacketEvents;
-import com.github.retrooper.packetevents.event.PacketListenerAbstract;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
@@ -38,14 +38,7 @@
import java.util.Collections;
import java.util.List;
-/**
- * Listens for EntityEquipment events to apply modifications.
- *
- * @param
The platform type.
- */
-public class EntityEquipmentListener
extends PacketListenerAbstract {
- private final AHIPlatform
platform;
- private final ConfigManager
configManager;
+public class EquipmentSpoofer extends Spoofer implements PacketSpoofer {
private final boolean useDamageableInterface;
@@ -57,25 +50,12 @@ public class EntityEquipmentListener
extends PacketListenerAbstract {
.level(3)
.build());
- /**
- * Constructs a new EntityEquipmentListener with the specified platform.
- *
- * @param platform The platform to use.
- */
- public EntityEquipmentListener(AHIPlatform
platform) {
- this.platform = platform;
- this.configManager = platform.getConfigManager();
+ public EquipmentSpoofer(AHIPlayer player) {
+ super(player);
this.useDamageableInterface = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13);
-
- platform.getLogManager().debug("Entity Equipment listener initialized.");
}
- /**
- * Called when a packet is sent to the player
- *
- * @param event the packet sends event
- */
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getPacketType() != PacketType.Play.Server.ENTITY_EQUIPMENT) return;
@@ -84,11 +64,6 @@ public void onPacketSend(PacketSendEvent event) {
if (!settings.getItems().isEnabled()) return;
WrapperPlayServerEntityEquipment packet = new WrapperPlayServerEntityEquipment(event);
-
- if (settings.isAllowBypass()) {
- if (platform.hasPermission(event.getUser().getUUID(), "AntiHealthIndicator.Bypass")) return;
- }
-
List equipmentList = packet.getEquipment();
if (equipmentList.isEmpty()) {
return;
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/FoodSaturationSpoofer.java b/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/FoodSaturationSpoofer.java
new file mode 100644
index 0000000..52c50c9
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/FoodSaturationSpoofer.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.spoofers.impl;
+
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
+import com.deathmotion.antihealthindicator.data.Settings;
+import com.deathmotion.antihealthindicator.spoofers.Spoofer;
+import com.deathmotion.antihealthindicator.spoofers.type.PacketSpoofer;
+import com.github.retrooper.packetevents.event.PacketSendEvent;
+import com.github.retrooper.packetevents.protocol.packettype.PacketType;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateHealth;
+
+public class FoodSaturationSpoofer extends Spoofer implements PacketSpoofer {
+
+ public FoodSaturationSpoofer(AHIPlayer player) {
+ super(player);
+ }
+
+ @Override
+ public void onPacketSend(PacketSendEvent event) {
+ if (event.getPacketType() != PacketType.Play.Server.UPDATE_HEALTH) return;
+
+ final Settings settings = configManager.getSettings();
+ if (!settings.isFoodSaturation()) return;
+
+ WrapperPlayServerUpdateHealth packet = new WrapperPlayServerUpdateHealth(event);
+
+ if (packet.getFoodSaturation() > 0) {
+ packet.setFoodSaturation(Float.NaN);
+ event.markForReEncode(true);
+ }
+ }
+}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/MetadataSpoofer.java b/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/MetadataSpoofer.java
new file mode 100644
index 0000000..6a2280c
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/MetadataSpoofer.java
@@ -0,0 +1,221 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.spoofers.impl;
+
+import com.deathmotion.antihealthindicator.cache.EntityCache;
+import com.deathmotion.antihealthindicator.cache.entities.CachedEntity;
+import com.deathmotion.antihealthindicator.cache.entities.WolfEntity;
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
+import com.deathmotion.antihealthindicator.data.Settings;
+import com.deathmotion.antihealthindicator.spoofers.Spoofer;
+import com.deathmotion.antihealthindicator.spoofers.type.PacketSpoofer;
+import com.github.retrooper.packetevents.PacketEvents;
+import com.github.retrooper.packetevents.event.PacketSendEvent;
+import com.github.retrooper.packetevents.manager.server.ServerVersion;
+import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
+import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
+import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
+import com.github.retrooper.packetevents.protocol.packettype.PacketType;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityMetadata;
+
+public class MetadataSpoofer extends Spoofer implements PacketSpoofer {
+
+ // Constants for Iron Golem health thresholds
+ private static final float IRON_GOLEM_HEALTH_MAX = 100f;
+ private static final float IRON_GOLEM_THRESHOLD_1 = 74f;
+ private static final float IRON_GOLEM_THRESHOLD_2 = 49f;
+ private static final float IRON_GOLEM_THRESHOLD_3 = 24f;
+
+ private final EntityCache entityCache;
+ private final boolean healthTexturesSupported;
+
+ public MetadataSpoofer(AHIPlayer player) {
+ super(player);
+ this.entityCache = player.entityCache;
+ this.healthTexturesSupported = PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_15);
+ }
+
+ @Override
+ public void onPacketSend(PacketSendEvent event) {
+ if (!event.getPacketType().equals(PacketType.Play.Server.ENTITY_METADATA)) return;
+
+ Settings settings = configManager.getSettings();
+ if (!settings.getEntityData().isEnabled()) return;
+
+ WrapperPlayServerEntityMetadata packet = new WrapperPlayServerEntityMetadata(event);
+ int entityId = packet.getEntityId();
+
+ // Skip processing if the packet refers to the user's own entity.
+ if (entityId == player.user.getEntityId()) return;
+
+ CachedEntity cachedEntity = entityCache.getCachedEntity(entityId).orElse(null);
+ if (cachedEntity == null) return;
+
+ EntityType entityType = cachedEntity.getEntityType();
+ if (shouldIgnoreEntity(entityType, entityId, cachedEntity, settings)) return;
+
+ // Process each metadata entry for spoofing.
+ packet.getEntityMetadata().forEach(entityData -> handleEntityMetadata(entityType, entityData, settings));
+
+ event.markForReEncode(true);
+ }
+
+ private boolean shouldIgnoreEntity(EntityType entityType, int entityId, CachedEntity cachedEntity, Settings settings) {
+ // Ignore entities with built-in health displays (e.g., bosses).
+ if (entityType == EntityTypes.WITHER || entityType == EntityTypes.ENDER_DRAGON) {
+ return true;
+ }
+
+ // If only players should be processed, skip non-player entities.
+ if (settings.getEntityData().isPlayersOnly() && entityType != EntityTypes.PLAYER) {
+ return true;
+ }
+
+ // Optionally ignore vehicles.
+ if (!settings.getEntityData().isPlayersOnly() && settings.getEntityData().isIgnoreVehicles() && entityCache.isUserPassenger(entityId)) {
+ return true;
+ }
+
+ // Special handling for wolves.
+ if (entityType == EntityTypes.WOLF && settings.getEntityData().getWolves().isEnabled()) {
+ return shouldIgnoreWolf(cachedEntity, settings);
+ }
+
+ return false;
+ }
+
+ /**
+ * Determines whether a wolf entity should be ignored based on its tamed/owner state and the settings.
+ */
+ private boolean shouldIgnoreWolf(CachedEntity cachedEntity, Settings settings) {
+ WolfEntity wolfEntity = (WolfEntity) cachedEntity;
+ Settings.EntityData.Wolves wolfSettings = settings.getEntityData().getWolves();
+
+ // If neither tamed nor owner conditions are enabled, ignore the wolf.
+ if (!wolfSettings.isTamed() && !wolfSettings.isOwner()) {
+ return true;
+ }
+ // Ignore if the wolf is tamed and tamed wolves should be ignored.
+ if (wolfSettings.isTamed() && wolfEntity.isTamed()) {
+ return true;
+ }
+
+ // Ignore if the user owns the wolf and owner wolves should be ignored.
+ return wolfSettings.isOwner() && wolfEntity.isOwnerPresent() && wolfEntity.getOwnerUUID().equals(player.uuid);
+ }
+
+ /**
+ * Modifies the metadata for the given entity based on its type and the configured settings.
+ */
+ private void handleEntityMetadata(EntityType entityType, EntityData entityData, Settings settings) {
+ if (entityType == EntityTypes.IRON_GOLEM && settings.getEntityData().getIronGolems().isEnabled()) {
+ if (!settings.getEntityData().getIronGolems().isGradual() || !healthTexturesSupported) {
+ applyDefaultSpoofing(entityData, settings);
+ } else {
+ spoofIronGolemMetadata(entityData, settings);
+ }
+ } else {
+ applyDefaultSpoofing(entityData, settings);
+ if (entityType == EntityTypes.PLAYER) {
+ spoofPlayerMetadata(entityData, settings);
+ }
+ }
+ }
+
+ /**
+ * Applies default spoofing logic to common metadata.
+ */
+ private void applyDefaultSpoofing(EntityData entityData, Settings settings) {
+ updateAirTicks(entityData, settings);
+ if (entityData.getIndex() == player.metadataIndex.HEALTH && settings.getEntityData().isHealth()) {
+ float health = (Float) entityData.getValue();
+ if (health > 0) {
+ // Spoof health value to a fixed, low value.
+ entityData.setValue(0.5f);
+ }
+ }
+ }
+
+ /**
+ * Applies gradual spoofing for iron golem health based on thresholds.
+ */
+ private void spoofIronGolemMetadata(EntityData entityData, Settings settings) {
+ updateAirTicks(entityData, settings);
+ if (entityData.getIndex() == player.metadataIndex.HEALTH && settings.getEntityData().isHealth()) {
+ float health = (Float) entityData.getValue();
+ if (health > IRON_GOLEM_THRESHOLD_1) {
+ entityData.setValue(IRON_GOLEM_HEALTH_MAX);
+ } else if (health > IRON_GOLEM_THRESHOLD_2) {
+ entityData.setValue(IRON_GOLEM_THRESHOLD_1);
+ } else if (health > IRON_GOLEM_THRESHOLD_3) {
+ entityData.setValue(IRON_GOLEM_THRESHOLD_2);
+ } else {
+ entityData.setValue(IRON_GOLEM_THRESHOLD_3);
+ }
+ }
+ }
+
+ /**
+ * Spoofs player-specific metadata such as absorption and experience.
+ */
+ private void spoofPlayerMetadata(EntityData entityData, Settings settings) {
+ if (entityData.getIndex() == player.metadataIndex.ABSORPTION && settings.getEntityData().isAbsorption()) {
+ setDynamicValue(entityData, 0);
+ }
+ if (entityData.getIndex() == player.metadataIndex.XP && settings.getEntityData().isXp()) {
+ setDynamicValue(entityData, 0);
+ }
+ }
+
+ /**
+ * Updates the air ticks metadata if enabled in the settings.
+ */
+ private void updateAirTicks(EntityData entityData, Settings settings) {
+ if (entityData.getIndex() == player.metadataIndex.AIR_TICKS && settings.getEntityData().isAirTicks()) {
+ setDynamicValue(entityData, 1);
+ }
+ }
+
+ /**
+ * Sets a new value for the entity data while preserving its original numeric type.
+ *
+ * This method is necessary because the metadata value is stored as an {@code Object}
+ * and can be of different numeric types (e.g., Integer, Short, Byte, Long, Float, or Double).
+ *
+ * @param entityData The metadata object to modify.
+ * @param spoofValue The new value to set.
+ */
+ private void setDynamicValue(EntityData entityData, int spoofValue) {
+ Object value = entityData.getValue();
+
+ if (value instanceof Integer) {
+ entityData.setValue(spoofValue);
+ } else if (value instanceof Short) {
+ entityData.setValue((short) spoofValue);
+ } else if (value instanceof Byte) {
+ entityData.setValue((byte) spoofValue);
+ } else if (value instanceof Long) {
+ entityData.setValue((long) spoofValue);
+ } else if (value instanceof Float) {
+ entityData.setValue((float) spoofValue);
+ } else if (value instanceof Double) {
+ entityData.setValue((double) spoofValue);
+ }
+ }
+}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/ScoreboardListener.java b/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/ScoreboardSpoofer.java
similarity index 59%
rename from common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/ScoreboardListener.java
rename to common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/ScoreboardSpoofer.java
index 415ac3e..bc90827 100644
--- a/common/src/main/java/com/deathmotion/antihealthindicator/packetlisteners/spoofers/ScoreboardListener.java
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/ScoreboardSpoofer.java
@@ -1,27 +1,27 @@
/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
-package com.deathmotion.antihealthindicator.packetlisteners.spoofers;
+package com.deathmotion.antihealthindicator.spoofers.impl;
-import com.deathmotion.antihealthindicator.AHIPlatform;
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
import com.deathmotion.antihealthindicator.data.Settings;
-import com.deathmotion.antihealthindicator.managers.ConfigManager;
-import com.github.retrooper.packetevents.event.PacketListenerAbstract;
+import com.deathmotion.antihealthindicator.spoofers.Spoofer;
+import com.deathmotion.antihealthindicator.spoofers.type.PacketSpoofer;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerScoreboardObjective;
@@ -31,21 +31,12 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-public class ScoreboardListener
extends PacketListenerAbstract {
- private final ConfigManager
configManager;
+public class ScoreboardSpoofer extends Spoofer implements PacketSpoofer {
- // Use ConcurrentHashMap's KeySet for a thread-safe Set
private final Set healthObjectives = ConcurrentHashMap.newKeySet();
- /**
- * Constructs a new EntityMetadataListener with the specified {@link AHIPlatform}.
- *
- * @param platform The platform to use.
- */
- public ScoreboardListener(AHIPlatform platform) {
- this.configManager = platform.getConfigManager();
-
- platform.getLogManager().debug("Update Objective Listener initialized.");
+ public ScoreboardSpoofer(AHIPlayer player) {
+ super(player);
}
@Override
@@ -91,4 +82,4 @@ private void handleUpdateScore(PacketSendEvent event) {
event.markForReEncode(true);
}
}
-}
\ No newline at end of file
+}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/WorldSeedSpoofer.java b/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/WorldSeedSpoofer.java
new file mode 100644
index 0000000..6d3c892
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/impl/WorldSeedSpoofer.java
@@ -0,0 +1,52 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.spoofers.impl;
+
+import com.deathmotion.antihealthindicator.data.AHIPlayer;
+import com.deathmotion.antihealthindicator.data.Settings;
+import com.deathmotion.antihealthindicator.spoofers.Spoofer;
+import com.deathmotion.antihealthindicator.spoofers.type.PacketSpoofer;
+import com.github.retrooper.packetevents.event.PacketSendEvent;
+import com.github.retrooper.packetevents.protocol.packettype.PacketType;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerJoinGame;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRespawn;
+
+public class WorldSeedSpoofer extends Spoofer implements PacketSpoofer {
+
+ public WorldSeedSpoofer(AHIPlayer player) {
+ super(player);
+ }
+
+ @Override
+ public void onPacketSend(PacketSendEvent event) {
+ final Settings settings = configManager.getSettings();
+ if (!settings.isWorldSeed()) return;
+
+ if (event.getPacketType().equals(PacketType.Play.Server.JOIN_GAME)) {
+ WrapperPlayServerJoinGame wrapper = new WrapperPlayServerJoinGame(event);
+ wrapper.setHashedSeed(0L);
+ event.markForReEncode(true);
+ }
+ if (event.getPacketType().equals(PacketType.Play.Server.RESPAWN)) {
+ WrapperPlayServerRespawn wrapper = new WrapperPlayServerRespawn(event);
+ wrapper.setHashedSeed(0L);
+ event.markForReEncode(true);
+ }
+ }
+}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/type/PacketSpoofer.java b/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/type/PacketSpoofer.java
new file mode 100644
index 0000000..3bdecb9
--- /dev/null
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/spoofers/type/PacketSpoofer.java
@@ -0,0 +1,26 @@
+/*
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.deathmotion.antihealthindicator.spoofers.type;
+
+import com.github.retrooper.packetevents.event.PacketSendEvent;
+
+public interface PacketSpoofer {
+ default void onPacketSend(final PacketSendEvent event) {
+ }
+}
diff --git a/common/src/main/java/com/deathmotion/antihealthindicator/managers/UpdateManager.java b/common/src/main/java/com/deathmotion/antihealthindicator/util/UpdateChecker.java
similarity index 66%
rename from common/src/main/java/com/deathmotion/antihealthindicator/managers/UpdateManager.java
rename to common/src/main/java/com/deathmotion/antihealthindicator/util/UpdateChecker.java
index 2afc0be..0b51d47 100644
--- a/common/src/main/java/com/deathmotion/antihealthindicator/managers/UpdateManager.java
+++ b/common/src/main/java/com/deathmotion/antihealthindicator/util/UpdateChecker.java
@@ -1,35 +1,38 @@
/*
- * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
- * Copyright (C) 2025 Bram and contributors
+ * This file is part of AntiHealthIndicator - https://github.com/Bram1903/AntiHealthIndicator
+ * Copyright (C) 2025 Bram and contributors
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
-package com.deathmotion.antihealthindicator.managers;
+package com.deathmotion.antihealthindicator.util;
import com.deathmotion.antihealthindicator.AHIPlatform;
import com.deathmotion.antihealthindicator.api.versioning.AHIVersion;
import com.deathmotion.antihealthindicator.data.Constants;
import com.deathmotion.antihealthindicator.data.Settings;
-import com.deathmotion.antihealthindicator.packetlisteners.UpdateNotifier;
-import com.deathmotion.antihealthindicator.util.AHIVersions;
-import com.github.retrooper.packetevents.PacketEvents;
+import com.deathmotion.antihealthindicator.managers.LogManager;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
+import lombok.Getter;
import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.event.ClickEvent;
+import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.format.TextDecoration;
+import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -37,12 +40,16 @@
import java.net.URLConnection;
import java.util.concurrent.CompletableFuture;
-public class UpdateManager
{
+public class UpdateChecker
{
private final AHIPlatform
platform;
private final Settings settings;
private final LogManager
logManager;
- public UpdateManager(AHIPlatform
platform) {
+ @Getter
+ private boolean updateAvailable = false;
+ private @Nullable AHIVersion latestVersion;
+
+ public UpdateChecker(AHIPlatform
platform) {
this.platform = platform;
this.settings = platform.getConfigManager().getSettings();
this.logManager = platform.getLogManager();
@@ -56,7 +63,7 @@ public void checkForUpdate() {
CompletableFuture.runAsync(() -> {
try {
AHIVersion localVersion = AHIVersions.CURRENT;
- AHIVersion latestVersion = fetchLatestGitHubVersion();
+ latestVersion = fetchLatestGitHubVersion();
if (latestVersion != null) {
handleVersionComparison(localVersion, latestVersion);
@@ -101,9 +108,8 @@ private void notifyUpdateAvailable(AHIVersion currentVersion, AHIVersion newVers
.append(Component.text(" | New version: ", NamedTextColor.WHITE))
.append(Component.text(newVersion.toStringWithoutSnapshot(), NamedTextColor.DARK_PURPLE)));
}
- if (settings.getUpdateChecker().isNotifyInGame()) {
- PacketEvents.getAPI().getEventManager().registerListener(new UpdateNotifier<>(platform, newVersion));
- }
+
+ updateAvailable = true;
}
private void notifyOnDevBuild(AHIVersion currentVersion, AHIVersion newVersion) {
@@ -116,4 +122,17 @@ private void notifyOnDevBuild(AHIVersion currentVersion, AHIVersion newVersion)
.append(Component.text(newVersion.toStringWithoutSnapshot(), NamedTextColor.DARK_AQUA)));
}
}
+
+ public Component getUpdateComponent() {
+ return Component.text()
+ .append(Component.text("[AntiHealthIndicator] ", NamedTextColor.RED)
+ .decoration(TextDecoration.BOLD, true))
+ .append(Component.text("Version " + latestVersion.toStringWithoutSnapshot() + " is ", NamedTextColor.GREEN))
+ .append(Component.text("now available", NamedTextColor.GREEN)
+ .decorate(TextDecoration.UNDERLINED)
+ .hoverEvent(HoverEvent.showText(Component.text("Click to download", NamedTextColor.GREEN)))
+ .clickEvent(ClickEvent.openUrl(Constants.MODRINTH_URL)))
+ .append(Component.text("!", NamedTextColor.GREEN))
+ .build();
+ }
}
\ No newline at end of file
diff --git a/common/src/main/resources/config.yml b/common/src/main/resources/config.yml
index 8b5f86d..691cfd9 100644
--- a/common/src/main/resources/config.yml
+++ b/common/src/main/resources/config.yml
@@ -20,10 +20,6 @@ update-checker:
# When an update is found, should players with the permission "AntiHealthIndicator.Notify" be notified?
notify-in-game: true
-# If enabled, players with the permission "AntiHealthIndicator.Bypass" will not be affected by the spoofing.
-allow-bypass:
- enabled: false
-
# These options determine what information should be spoofed.
spoof:
# Sets the seed which is being sent to the client to zero.
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 68265b4..9886238 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,6 +2,7 @@
adventure = "4.18.0"
packetevents = "2.7.0"
betterreload = "v1.0.0"
+guava = "33.3.1-jre"
paper = "1.21.4-R0.1-SNAPSHOT"
velocity = "3.4.0-SNAPSHOT"
bungeecord = "1.21-R0.1-SNAPSHOT"
@@ -26,6 +27,7 @@ packetevents-sponge = { group = "com.github.retrooper", name = "packetevents-spo
snakeyaml = { group = "org.yaml", name = "snakeyaml", version.ref = "snakeyaml" }
lombok = { group = "org.projectlombok", name = "lombok", version.ref = "lombok" }
betterreload-api = { group = "com.github.amnoah.betterreload", name = "api", version.ref = "betterreload" }
+guava = { group = "com.google.guava", name = "guava", version.ref = "guava" }
paper = { group = "io.papermc.paper", name = "paper-api", version.ref = "paper" }
velocity = { group = "com.velocitypowered", name = "velocity-api", version.ref = "velocity" }
bungeecord = { group = "net.md-5", name = "bungeecord-api", version.ref = "bungeecord" }