Skip to content

Commit

Permalink
Merge pull request #15 from Estecka/healthbarInit+1.20
Browse files Browse the repository at this point in the history
Fix missing healthbar in freshly loaded chunks.
  • Loading branch information
samolego authored Nov 7, 2023
2 parents a8ca267 + 9da0bb1 commit 075a469
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 43 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ loader_version=0.14.21
#Fabric api
fabric_version=0.83.0+1.20
# Mod Properties
mod_version=1.3.1
mod_version=1.3.3
maven_group=org.samo_lego
archives_base_name=healthcare
# Dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBundlePacket;
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerPlayer;
Expand All @@ -23,6 +25,7 @@

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import static org.samo_lego.healthcare.HealthCare.config;
Expand Down Expand Up @@ -66,53 +69,85 @@ public abstract class ServerPlayNetworkHandlerMixin_HealthTag {
cancellable = true
)
private void onPacketSend(Packet<?> sendPacket, PacketSendListener listener, CallbackInfo ci) {
if (sendPacket instanceof ClientboundSetEntityDataPacket packet && !this.hc_skipCheck) {
int id = packet.id();
Entity entity = this.player.level().getEntity(id);
final var hb = ((HealthbarPreferences) this.player).healthcarePrefs();

if (entity instanceof LivingEntity living &&
hb.enabled &&
!(entity instanceof Player) &&
!config.blacklistedEntities.contains(BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString()) &&
!entity.isInvisibleTo(player)) {

var trackedValues = new ArrayList<>(packet.packedItems());

// Removing current custom name
var customName = trackedValues.stream().filter(value -> value.id() == 2).findFirst();

// Ensure name is visible only if mob is not too far away
boolean visible = (entity.distanceTo(player) < config.activationRange || entity.isCustomNameVisible()) && hb.alwaysVisible;
var visibleTag = SynchedEntityData.DataValue.create(EntityAccessor.getNAME_VISIBLE(), visible);

float health = living.getHealth();
float maxHealth = living.getMaxHealth();

MutableComponent name = Component.empty();
if (customName.isPresent() && ((Optional<Component>) customName.get().value()).isPresent()) {
name = ((Optional<Component>) customName.get().value()).get().copy().append(" ");
} else if (entity.hasCustomName()) {
// @SpaceClouds42 saved me here, `.copy()` after getting custom name is essential!
name = entity.getCustomName().copy().append(" ");
} else if (hb.showType) {
name = Component.translatable(entity.getType().getDescriptionId()).append(" ");
Packet<?> mutatedPacket = null;

if (hc_skipCheck)
return;

if (sendPacket instanceof ClientboundSetEntityDataPacket dataPacket)
mutatedPacket = TryMutatePacket(dataPacket);
else if (sendPacket instanceof ClientboundBundlePacket bundledPackets){
List<Packet<ClientGamePacketListener>> packets = new ArrayList<>();
boolean doMutate = false;

for (var p : bundledPackets.subPackets()){
if (p instanceof ClientboundSetEntityDataPacket dataPacket && (dataPacket=TryMutatePacket(dataPacket)) != null){
doMutate = true;
packets.add(dataPacket);
}
else
packets.add(p);
}

var healthbar = ((HealthbarPreferences) this.player).createHealthbarText(health, maxHealth);
var healthTag = SynchedEntityData.DataValue.create(EntityAccessor.getCUSTOM_NAME(), Optional.of(name.append(healthbar)));
if (doMutate)
mutatedPacket = new ClientboundBundlePacket(packets);
}

Collections.addAll(trackedValues, visibleTag, healthTag);
if (mutatedPacket != null) {
this.hc_skipCheck = true;
this.send(mutatedPacket, listener);
this.hc_skipCheck = false;
ci.cancel(); // cancel the original packet going out
}
}

// Create a new packet in order to not mess with other network handlers
// since same packet object is sent to every player
var trackerUpdatePacket = new ClientboundSetEntityDataPacket(id, trackedValues);
/**
* @return A new packet, or null if the original needed not be mutated.
*/
@Unique
private @Nullable ClientboundSetEntityDataPacket TryMutatePacket(ClientboundSetEntityDataPacket packet) {
int id = packet.id();
Entity entity = this.player.level().getEntity(id);
final var hb = ((HealthbarPreferences) this.player).healthcarePrefs();

if (!hb.enabled
|| !(entity instanceof LivingEntity living)
|| entity instanceof Player
|| config.blacklistedEntities.contains(BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString())
|| entity.isInvisibleTo(player)
) {
return null;
}

this.hc_skipCheck = true;
this.send(trackerUpdatePacket, listener);
this.hc_skipCheck = false;
ci.cancel(); // cancel the original packet going out
}
var trackedValues = new ArrayList<>(packet.packedItems());

// Removing current custom name
var customName = trackedValues.stream().filter(value -> value.id() == 2).findFirst();

// Ensure name is visible only if mob is not too far away
boolean visible = (entity.distanceTo(player) < config.activationRange || entity.isCustomNameVisible()) && hb.alwaysVisible;
var visibleTag = SynchedEntityData.DataValue.create(EntityAccessor.getNAME_VISIBLE(), visible);

float health = living.getHealth();
float maxHealth = living.getMaxHealth();

MutableComponent name = Component.empty();
if (customName.isPresent() && ((Optional<Component>) customName.get().value()).isPresent()) {
name = ((Optional<Component>) customName.get().value()).get().copy().append(" ");
} else if (entity.hasCustomName()) {
// @SpaceClouds42 saved me here, `.copy()` after getting custom name is essential!
name = entity.getCustomName().copy().append(" ");
} else if (hb.showType) {
name = Component.translatable(entity.getType().getDescriptionId()).append(" ");
}

var healthbar = ((HealthbarPreferences) this.player).createHealthbarText(health, maxHealth);
var healthTag = SynchedEntityData.DataValue.create(EntityAccessor.getCUSTOM_NAME(), Optional.of(name.append(healthbar)));

Collections.addAll(trackedValues, visibleTag, healthTag);

// Create a new packet in order to not mess with other network handlers
// since same packet object is sent to every player
return new ClientboundSetEntityDataPacket(id, trackedValues);
}
}

0 comments on commit 075a469

Please sign in to comment.