Commit bb000ace by Jonathan Thomas

- Wither now drops a Nether Star at max friendship (for pacifists)

- Broadcasting and receiving chat messages now ignores if the UUID is valid (to keep data synced)
- Bees no longer forget their chat data when entering/leaving hives (writeNbt & readNbt modified)
parent a9c7b69f
Pipeline #13341 passed with stages
in 3 minutes 2 seconds
......@@ -4,6 +4,17 @@ All notable changes to **CreatureChat** are documented in this file. The format
[Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Added
- Wither now drops a Nether Star at max friendship (for pacifists)
### Changed
- Broadcasting and receiving chat messages now ignores if the UUID is valid (to keep data synced)
### Fixed
- Bees no longer forget their chat data when entering/leaving hives (writeNbt & readNbt modified)
## [1.3.0] - 2025-01-14
### Added
......
......@@ -127,16 +127,9 @@ public class ClientPackets {
return;
}
// Update the chat data manager on the client-side
MobEntity entity = ClientEntityFinder.getEntityByUUID(client.world, entityId);
if (entity == null) {
LOGGER.warn("Entity with ID '{}' not found. Skipping message processing.", entityId);
return;
}
// Get entity chat data for current entity & player
ChatDataManager chatDataManager = ChatDataManager.getClientInstance();
EntityChatData chatData = chatDataManager.getOrCreateChatData(entity.getUuidAsString());
EntityChatData chatData = chatDataManager.getOrCreateChatData(entityId.toString());
// Add entity message
if (!message.isEmpty()) {
......@@ -148,7 +141,10 @@ public class ClientPackets {
chatData.players = players;
// Play sound with volume based on distance (from player or entity)
playNearbyUISound(client, entity, 0.2f);
MobEntity entity = ClientEntityFinder.getEntityByUUID(client.world, entityId);
if (entity != null) {
playNearbyUISound(client, entity, 0.2f);
}
});
});
......
......@@ -13,7 +13,9 @@ import com.owlmaddie.particle.ParticleEmitter;
import com.owlmaddie.utils.Randomizer;
import com.owlmaddie.utils.ServerEntityFinder;
import com.owlmaddie.utils.VillagerEntityAccessor;
import com.owlmaddie.utils.WitherEntityAccessor;
import net.minecraft.entity.ExperienceOrbEntity;
import net.minecraft.entity.boss.WitherEntity;
import net.minecraft.entity.boss.dragon.EnderDragonEntity;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.passive.TameableEntity;
......@@ -429,6 +431,13 @@ public class EntityChatData {
EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class);
EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class);
if (entity instanceof WitherEntity && new_friendship == 3) {
// Best friend a Nether and get a NETHER_STAR
WitherEntity wither = (WitherEntity) entity;
((WitherEntityAccessor) wither).callDropEquipment(entity.getWorld().getDamageSources().generic(), 1, true);
entity.getWorld().playSound(entity, entity.getBlockPos(), SoundEvents.ENTITY_WITHER_DEATH, SoundCategory.PLAYERS, 0.3F, 1.0F);
}
if (entity instanceof EnderDragonEntity && new_friendship == 3) {
// Trigger end of game (friendship always wins!)
EnderDragonEntity dragon = (EnderDragonEntity) entity;
......
package com.owlmaddie.mixin;
import com.owlmaddie.chat.ChatDataManager;
import com.owlmaddie.chat.EntityChatData;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.NbtCompound;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.UUID;
@Mixin(Entity.class)
public abstract class MixinEntityChatData {
@Shadow
public abstract UUID getUuid();
/**
* When writing NBT data, if the entity has chat data then store its UUID under "CCUUID".
*/
@Inject(method = "writeNbt", at = @At("TAIL"))
private void writeChatData(NbtCompound nbt, CallbackInfoReturnable<NbtCompound> cir) {
UUID currentUUID = this.getUuid();
// Retrieve or create the chat data for this entity.
EntityChatData chatData = ChatDataManager.getServerInstance().getOrCreateChatData(currentUUID.toString());
// If the entity actually has chat data (for example, if its character sheet is non-empty), add CCUUID.
if (!chatData.characterSheet.isEmpty()) {
// Note: cir.getReturnValue() returns the NBT compound the method is about to return.
cir.getReturnValue().putUuid("CCUUID", currentUUID);
}
}
/**
* When reading NBT data, if there is a "CCUUID" entry and it does not match the entity’s current UUID,
* update our chat data key to reflect the change.
*/
@Inject(method = "readNbt", at = @At("TAIL"))
private void readChatData(NbtCompound nbt, CallbackInfo ci) {
UUID currentUUID = this.getUuid();
if (nbt.contains("CCUUID")) {
UUID originalUUID = nbt.getUuid("CCUUID");
if (!originalUUID.equals(currentUUID)) {
ChatDataManager.getServerInstance().updateUUID(originalUUID.toString(), currentUUID.toString());
}
}
}
}
package com.owlmaddie.mixin;
import com.owlmaddie.utils.WitherEntityAccessor;
import net.minecraft.entity.boss.WitherEntity;
import net.minecraft.entity.damage.DamageSource;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
/**
* Mixin to expose the protected dropEquipment method from WitherEntity.
*/
@Mixin(WitherEntity.class)
public abstract class MixinWitherEntity implements WitherEntityAccessor {
@Shadow
protected abstract void dropEquipment(DamageSource source, int lootingMultiplier, boolean allowDrops);
@Override
public void callDropEquipment(DamageSource source, int lootingMultiplier, boolean allowDrops) {
dropEquipment(source, lootingMultiplier, allowDrops);
}
}
......@@ -351,10 +351,10 @@ public class ServerPackets {
chatData.currentLineNumber, chatData.sender);
for (ServerWorld world : serverInstance.getWorlds()) {
// Find Entity by UUID and update custom name
UUID entityId = UUID.fromString(chatData.entityId);
MobEntity entity = (MobEntity)ServerEntityFinder.getEntityByUUID(world, entityId);
if (entity != null) {
// Set custom name (if null)
String characterName = chatData.getCharacterProp("name");
if (!characterName.isEmpty() && !characterName.equals("N/A") && entity.getCustomName() == null) {
LOGGER.debug("Setting entity name to " + characterName + " for " + chatData.entityId);
......@@ -362,27 +362,27 @@ public class ServerPackets {
entity.setCustomNameVisible(true);
entity.setPersistent();
}
}
// Make auto-generated message appear as a pending icon (attack, show/give, arrival)
if (chatData.sender == ChatDataManager.ChatSender.USER && chatData.auto_generated > 0) {
chatData.status = ChatDataManager.ChatStatus.PENDING;
}
// Make auto-generated message appear as a pending icon (attack, show/give, arrival)
if (chatData.sender == ChatDataManager.ChatSender.USER && chatData.auto_generated > 0) {
chatData.status = ChatDataManager.ChatStatus.PENDING;
}
// Iterate over all players and send the packet
for (ServerPlayerEntity player : serverInstance.getPlayerManager().getPlayerList()) {
PacketByteBuf buffer = new PacketByteBuf(Unpooled.buffer());
buffer.writeString(chatData.entityId);
buffer.writeString(chatData.currentMessage);
buffer.writeInt(chatData.currentLineNumber);
buffer.writeString(chatData.status.toString());
buffer.writeString(chatData.sender.toString());
writePlayerDataMap(buffer, chatData.players);
// Send message to player
ServerPlayNetworking.send(player, PACKET_S2C_ENTITY_MESSAGE, buffer);
}
break;
// Iterate over all players and send the packet
for (ServerPlayerEntity player : serverInstance.getPlayerManager().getPlayerList()) {
PacketByteBuf buffer = new PacketByteBuf(Unpooled.buffer());
buffer.writeString(chatData.entityId);
buffer.writeString(chatData.currentMessage);
buffer.writeInt(chatData.currentLineNumber);
buffer.writeString(chatData.status.toString());
buffer.writeString(chatData.sender.toString());
writePlayerDataMap(buffer, chatData.players);
// Send message to player
ServerPlayNetworking.send(player, PACKET_S2C_ENTITY_MESSAGE, buffer);
}
break;
}
}
......
package com.owlmaddie.utils;
import net.minecraft.entity.damage.DamageSource;
/**
* Accessor interface for WitherEntity to allow calling dropEquipment externally.
*/
public interface WitherEntityAccessor {
void callDropEquipment(DamageSource source, int lootingMultiplier, boolean allowDrops);
}
......@@ -7,6 +7,8 @@
"MixinMobEntityAccessor",
"MixinLivingEntity",
"MixinBucketable",
"MixinEntityChatData",
"MixinWitherEntity",
"MixinVillagerEntity",
"MixinOnChat"
],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment