From 1e55dcba06ece075c07d38af8405334a6ce21c1c Mon Sep 17 00:00:00 2001 From: Jonathan Thomas <jonathan@openshot.org> Date: Sun, 23 Feb 2025 00:40:56 -0600 Subject: [PATCH] Improved error handling to prevent broken "..." pending chat status. (HTTP and message processing is more protected). Removed randomized error messages from chat history (so it doesn't break the chat history when an error is shown) --- CHANGELOG.md | 2 ++ src/main/java/com/owlmaddie/chat/ChatGPTRequest.java | 11 ++++++++--- src/main/java/com/owlmaddie/chat/EntityChatData.java | 435 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 3 files changed, 234 insertions(+), 214 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3b1270..4d1b07d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,13 @@ All notable changes to **CreatureChat** are documented in this file. The format ### Changed - Broadcasting and receiving chat messages now ignores if the UUID is valid (to keep data synced) +- Improved error handling to prevent broken "..." pending chat status. (HTTP and message processing is more protected) ### Fixed - Bees no longer forget their chat data when entering/leaving hives (writeNbt & readNbt modified) - Vexes no longer take damage when chat data exists - Wandering Trader no longer despawns if it has chat data +- Removed randomized error messages from chat history (so it doesn't break the chat history when an error is shown) ## [1.3.0] - 2025-01-14 diff --git a/src/main/java/com/owlmaddie/chat/ChatGPTRequest.java b/src/main/java/com/owlmaddie/chat/ChatGPTRequest.java index 3c87673..3142ec6 100644 --- a/src/main/java/com/owlmaddie/chat/ChatGPTRequest.java +++ b/src/main/java/com/owlmaddie/chat/ChatGPTRequest.java @@ -196,6 +196,7 @@ public class ChatGPTRequest { lastErrorMessage = cleanError; } catch (Exception e) { LOGGER.error("Failed to read error response", e); + lastErrorMessage = "Failed to read error response: " + e.getMessage(); } return null; } else { @@ -214,12 +215,16 @@ public class ChatGPTRequest { if (chatGPTResponse != null && chatGPTResponse.choices != null && !chatGPTResponse.choices.isEmpty()) { String content = chatGPTResponse.choices.get(0).message.content; return content; + } else { + lastErrorMessage = "Failed to parse response from LLM"; + return null; } } - } catch (IOException e) { - LOGGER.error("Failed to fetch message from ChatGPT", e); + } catch (Exception e) { + LOGGER.error("Failed to request message from LLM", e); + lastErrorMessage = "Failed to request message from LLM: " + e.getMessage(); + return null; } - return null; // If there was an error or no response, return null }); } } diff --git a/src/main/java/com/owlmaddie/chat/EntityChatData.java b/src/main/java/com/owlmaddie/chat/EntityChatData.java index f151ef2..cf1d6ec 100644 --- a/src/main/java/com/owlmaddie/chat/EntityChatData.java +++ b/src/main/java/com/owlmaddie/chat/EntityChatData.java @@ -273,34 +273,41 @@ public class EntityChatData { // fetch HTTP response from ChatGPT ChatGPTRequest.fetchMessageFromChatGPT(config, promptText, contextData, previousMessages, false).thenAccept(output_message -> { - if (output_message != null) { - // Character Sheet: Remove system-character message from previous messages - previousMessages.clear(); + try { + if (output_message != null) { + // Character Sheet: Remove system-character message from previous messages + previousMessages.clear(); + + // Add NEW CHARACTER sheet & greeting + this.characterSheet = output_message; + String shortGreeting = Optional.ofNullable(getCharacterProp("short greeting")).filter(s -> !s.isEmpty()).orElse(Randomizer.getRandomMessage(Randomizer.RandomType.NO_RESPONSE)).replace("\n", " "); + this.addMessage(shortGreeting, ChatDataManager.ChatSender.ASSISTANT, player, systemPrompt); + + } else { + // No valid LLM response + throw new RuntimeException(ChatGPTRequest.lastErrorMessage); + } - // Add NEW CHARACTER sheet & greeting - this.characterSheet = output_message; - String shortGreeting = Optional.ofNullable(getCharacterProp("short greeting")).filter(s -> !s.isEmpty()).orElse(Randomizer.getRandomMessage(Randomizer.RandomType.NO_RESPONSE)).replace("\n", " "); - this.addMessage(shortGreeting, ChatDataManager.ChatSender.ASSISTANT, player, systemPrompt); + } catch (Exception e) { + // Log the exception for debugging + LOGGER.error("Error processing LLM response", e); - } else { // Error / No Chat Message (Failure) String randomErrorMessage = Randomizer.getRandomMessage(Randomizer.RandomType.ERROR); this.addMessage(randomErrorMessage, ChatDataManager.ChatSender.ASSISTANT, player, systemPrompt); - // Determine error message to display - String errorMessage = "Help is available at discord.creaturechat.com"; - if (!ChatGPTRequest.lastErrorMessage.isEmpty()) { - errorMessage = "Error: " + truncateString(ChatGPTRequest.lastErrorMessage, 55) + "\n" + errorMessage; + // Remove the error message from history to prevent it from affecting future ChatGPT requests + if (!previousMessages.isEmpty()) { + previousMessages.remove(previousMessages.size() - 1); } // Send clickable error message - ServerPackets.SendClickableError(player, - errorMessage, "http://discord.creaturechat.com"); - - // Clear history (if no character sheet was generated) - if (characterSheet.isEmpty()) { - previousMessages.clear(); + String errorMessage = "Error: "; + if (e.getMessage() != null && !e.getMessage().isEmpty()) { + errorMessage += truncateString(e.getMessage(), 55) + "\n"; } + errorMessage += "Help is available at discord.creaturechat.com"; + ServerPackets.SendClickableError(player, errorMessage, "http://discord.creaturechat.com"); } }); } @@ -336,236 +343,242 @@ public class EntityChatData { // fetch HTTP response from ChatGPT ChatGPTRequest.fetchMessageFromChatGPT(config, promptText, contextData, previousMessages, false).thenAccept(output_message -> { - if (output_message != null) { - // Chat Message: Parse message for behaviors - ParsedMessage result = MessageParser.parseMessage(output_message.replace("\n", " ")); - MobEntity entity = (MobEntity)ServerEntityFinder.getEntityByUUID(player.getServerWorld(), UUID.fromString(entityId)); - - // Determine entity's default speed - // Some Entities (i.e. Axolotl) set this incorrectly... so adjusting in the SpeedControls class - float entitySpeed = SpeedControls.getMaxSpeed(entity); - float entitySpeedMedium = MathHelper.clamp(entitySpeed * 1.15F, 0.5f, 1.15f); - float entitySpeedFast = MathHelper.clamp(entitySpeed * 1.3F, 0.5f, 1.3f); - - // Apply behaviors (if any) - for (Behavior behavior : result.getBehaviors()) { - LOGGER.info("Behavior: " + behavior.getName() + (behavior.getArgument() != null ? - ", Argument: " + behavior.getArgument() : "")); - - // Apply behaviors to entity - if (behavior.getName().equals("FOLLOW")) { - FollowPlayerGoal followGoal = new FollowPlayerGoal(player, entity, entitySpeedMedium); - EntityBehaviorManager.removeGoal(entity, TalkPlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); - EntityBehaviorManager.addGoal(entity, followGoal, GoalPriority.FOLLOW_PLAYER); - if (playerData.friendship >= 0) { - ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, FOLLOW_FRIEND_PARTICLE, 0.5, 1); - } else { - ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, FOLLOW_ENEMY_PARTICLE, 0.5, 1); - } + try { + if (output_message != null) { + // Chat Message: Parse message for behaviors + ParsedMessage result = MessageParser.parseMessage(output_message.replace("\n", " ")); + MobEntity entity = (MobEntity) ServerEntityFinder.getEntityByUUID(player.getServerWorld(), UUID.fromString(entityId)); + + // Determine entity's default speed + // Some Entities (i.e. Axolotl) set this incorrectly... so adjusting in the SpeedControls class + float entitySpeed = SpeedControls.getMaxSpeed(entity); + float entitySpeedMedium = MathHelper.clamp(entitySpeed * 1.15F, 0.5f, 1.15f); + float entitySpeedFast = MathHelper.clamp(entitySpeed * 1.3F, 0.5f, 1.3f); + + // Apply behaviors (if any) + for (Behavior behavior : result.getBehaviors()) { + LOGGER.info("Behavior: " + behavior.getName() + (behavior.getArgument() != null ? + ", Argument: " + behavior.getArgument() : "")); + + // Apply behaviors to entity + if (behavior.getName().equals("FOLLOW")) { + FollowPlayerGoal followGoal = new FollowPlayerGoal(player, entity, entitySpeedMedium); + EntityBehaviorManager.removeGoal(entity, TalkPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); + EntityBehaviorManager.addGoal(entity, followGoal, GoalPriority.FOLLOW_PLAYER); + if (playerData.friendship >= 0) { + ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, FOLLOW_FRIEND_PARTICLE, 0.5, 1); + } else { + ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, FOLLOW_ENEMY_PARTICLE, 0.5, 1); + } - } else if (behavior.getName().equals("UNFOLLOW")) { - EntityBehaviorManager.removeGoal(entity, FollowPlayerGoal.class); - - } else if (behavior.getName().equals("FLEE")) { - float fleeDistance = 40F; - FleePlayerGoal fleeGoal = new FleePlayerGoal(player, entity, entitySpeedFast, fleeDistance); - EntityBehaviorManager.removeGoal(entity, TalkPlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, FollowPlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, ProtectPlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); - EntityBehaviorManager.addGoal(entity, fleeGoal, GoalPriority.FLEE_PLAYER); - ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, FLEE_PARTICLE, 0.5, 1); - - } else if (behavior.getName().equals("UNFLEE")) { - EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); - - } else if (behavior.getName().equals("ATTACK")) { - AttackPlayerGoal attackGoal = new AttackPlayerGoal(player, entity, entitySpeedFast); - EntityBehaviorManager.removeGoal(entity, TalkPlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, FollowPlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, ProtectPlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); - EntityBehaviorManager.addGoal(entity, attackGoal, GoalPriority.ATTACK_PLAYER); - ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, FLEE_PARTICLE, 0.5, 1); - - } else if (behavior.getName().equals("PROTECT")) { - if (playerData.friendship <= 0) { - // force friendship to prevent entity from attacking player when protecting - playerData.friendship = 1; - } - ProtectPlayerGoal protectGoal = new ProtectPlayerGoal(player, entity, 1.0); - EntityBehaviorManager.removeGoal(entity, TalkPlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); - EntityBehaviorManager.addGoal(entity, protectGoal, GoalPriority.PROTECT_PLAYER); - ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, PROTECT_PARTICLE, 0.5, 1); - - } else if (behavior.getName().equals("UNPROTECT")) { - EntityBehaviorManager.removeGoal(entity, ProtectPlayerGoal.class); - - } else if (behavior.getName().equals("LEAD")) { - LeadPlayerGoal leadGoal = new LeadPlayerGoal(player, entity, entitySpeedMedium); - EntityBehaviorManager.removeGoal(entity, FollowPlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); - EntityBehaviorManager.addGoal(entity, leadGoal, GoalPriority.LEAD_PLAYER); - if (playerData.friendship >= 0) { - ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, LEAD_FRIEND_PARTICLE, 0.5, 1); - } else { - ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, LEAD_ENEMY_PARTICLE, 0.5, 1); - } - } else if (behavior.getName().equals("UNLEAD")) { - EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); + } else if (behavior.getName().equals("UNFOLLOW")) { + EntityBehaviorManager.removeGoal(entity, FollowPlayerGoal.class); - } else if (behavior.getName().equals("FRIENDSHIP")) { - int new_friendship = Math.max(-3, Math.min(3, behavior.getArgument())); + } else if (behavior.getName().equals("FLEE")) { + float fleeDistance = 40F; + FleePlayerGoal fleeGoal = new FleePlayerGoal(player, entity, entitySpeedFast, fleeDistance); + EntityBehaviorManager.removeGoal(entity, TalkPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, FollowPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, ProtectPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); + EntityBehaviorManager.addGoal(entity, fleeGoal, GoalPriority.FLEE_PLAYER); + ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, FLEE_PARTICLE, 0.5, 1); - // Does friendship improve? - if (new_friendship > playerData.friendship) { - // Stop any attack/flee if friendship improves + } else if (behavior.getName().equals("UNFLEE")) { + EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); + + } else if (behavior.getName().equals("ATTACK")) { + AttackPlayerGoal attackGoal = new AttackPlayerGoal(player, entity, entitySpeedFast); + EntityBehaviorManager.removeGoal(entity, TalkPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, FollowPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, ProtectPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); + EntityBehaviorManager.addGoal(entity, attackGoal, GoalPriority.ATTACK_PLAYER); + ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, FLEE_PARTICLE, 0.5, 1); + + } else if (behavior.getName().equals("PROTECT")) { + if (playerData.friendship <= 0) { + // force friendship to prevent entity from attacking player when protecting + playerData.friendship = 1; + } + ProtectPlayerGoal protectGoal = new ProtectPlayerGoal(player, entity, 1.0); + EntityBehaviorManager.removeGoal(entity, TalkPlayerGoal.class); EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); + EntityBehaviorManager.addGoal(entity, protectGoal, GoalPriority.PROTECT_PLAYER); + ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, PROTECT_PARTICLE, 0.5, 1); + + } else if (behavior.getName().equals("UNPROTECT")) { + EntityBehaviorManager.removeGoal(entity, ProtectPlayerGoal.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); + } else if (behavior.getName().equals("LEAD")) { + LeadPlayerGoal leadGoal = new LeadPlayerGoal(player, entity, entitySpeedMedium); + EntityBehaviorManager.removeGoal(entity, FollowPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); + EntityBehaviorManager.addGoal(entity, leadGoal, GoalPriority.LEAD_PLAYER); + if (playerData.friendship >= 0) { + ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, LEAD_FRIEND_PARTICLE, 0.5, 1); + } else { + ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, LEAD_ENEMY_PARTICLE, 0.5, 1); } + } else if (behavior.getName().equals("UNLEAD")) { + EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); + + } else if (behavior.getName().equals("FRIENDSHIP")) { + int new_friendship = Math.max(-3, Math.min(3, behavior.getArgument())); + + // Does friendship improve? + if (new_friendship > playerData.friendship) { + // Stop any attack/flee if friendship improves + 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; + if (entity instanceof EnderDragonEntity && new_friendship == 3) { + // Trigger end of game (friendship always wins!) + EnderDragonEntity dragon = (EnderDragonEntity) entity; - // Emit particles & sound - ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, HEART_BIG_PARTICLE, 3, 200); - entity.getWorld().playSound(entity, entity.getBlockPos(), SoundEvents.ENTITY_ENDER_DRAGON_DEATH, SoundCategory.PLAYERS, 0.3F, 1.0F); - entity.getWorld().playSound(entity, entity.getBlockPos(), SoundEvents.UI_TOAST_CHALLENGE_COMPLETE, SoundCategory.PLAYERS, 0.5F, 1.0F); + // Emit particles & sound + ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, HEART_BIG_PARTICLE, 3, 200); + entity.getWorld().playSound(entity, entity.getBlockPos(), SoundEvents.ENTITY_ENDER_DRAGON_DEATH, SoundCategory.PLAYERS, 0.3F, 1.0F); + entity.getWorld().playSound(entity, entity.getBlockPos(), SoundEvents.UI_TOAST_CHALLENGE_COMPLETE, SoundCategory.PLAYERS, 0.5F, 1.0F); - // Check if the game rule for mob loot is enabled - boolean doMobLoot = entity.getWorld().getGameRules().getBoolean(GameRules.DO_MOB_LOOT); + // Check if the game rule for mob loot is enabled + boolean doMobLoot = entity.getWorld().getGameRules().getBoolean(GameRules.DO_MOB_LOOT); - // If this is the first time the dragon is 'befriended', adjust the XP - int baseXP = 500; - if (dragon.getFight() != null && !dragon.getFight().hasPreviouslyKilled()) { - baseXP = 12000; - } + // If this is the first time the dragon is 'befriended', adjust the XP + int baseXP = 500; + if (dragon.getFight() != null && !dragon.getFight().hasPreviouslyKilled()) { + baseXP = 12000; + } - // If the world is a server world and mob loot is enabled, spawn XP orbs - if (entity.getWorld() instanceof ServerWorld && doMobLoot) { - // Loop to spawn XP orbs - for (int j = 1; j <= 11; j++) { - float xpFraction = (j == 11) ? 0.2F : 0.08F; - int xpAmount = MathHelper.floor((float)baseXP * xpFraction); - ExperienceOrbEntity.spawn((ServerWorld)entity.getWorld(), entity.getPos(), xpAmount); + // If the world is a server world and mob loot is enabled, spawn XP orbs + if (entity.getWorld() instanceof ServerWorld && doMobLoot) { + // Loop to spawn XP orbs + for (int j = 1; j <= 11; j++) { + float xpFraction = (j == 11) ? 0.2F : 0.08F; + int xpAmount = MathHelper.floor((float) baseXP * xpFraction); + ExperienceOrbEntity.spawn((ServerWorld) entity.getWorld(), entity.getPos(), xpAmount); + } } - } - // Mark fight as over - dragon.getFight().dragonKilled(dragon); + // Mark fight as over + dragon.getFight().dragonKilled(dragon); + } } - } - // Merchant deals (if friendship changes with a Villager - if (entity instanceof VillagerEntity && playerData.friendship != new_friendship) { - VillagerEntityAccessor villager = (VillagerEntityAccessor) entity; - switch (new_friendship) { - case 3: - villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MAJOR_POSITIVE, 20); - villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MINOR_POSITIVE, 25); - break; - case 2: - villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MINOR_POSITIVE, 25); - break; - case 1: - villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MINOR_POSITIVE, 10); - break; - case -1: - villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MINOR_NEGATIVE, 10); - break; - case -2: - villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MINOR_NEGATIVE, 25); - break; - case -3: - villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MAJOR_NEGATIVE, 20); - villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MINOR_NEGATIVE, 25); - break; + // Merchant deals (if friendship changes with a Villager + if (entity instanceof VillagerEntity && playerData.friendship != new_friendship) { + VillagerEntityAccessor villager = (VillagerEntityAccessor) entity; + switch (new_friendship) { + case 3: + villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MAJOR_POSITIVE, 20); + villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MINOR_POSITIVE, 25); + break; + case 2: + villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MINOR_POSITIVE, 25); + break; + case 1: + villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MINOR_POSITIVE, 10); + break; + case -1: + villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MINOR_NEGATIVE, 10); + break; + case -2: + villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MINOR_NEGATIVE, 25); + break; + case -3: + villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MAJOR_NEGATIVE, 20); + villager.getGossip().startGossip(player.getUuid(), VillageGossipType.MINOR_NEGATIVE, 25); + break; + } } - } - // Tame best friends and un-tame worst enemies - if (entity instanceof TameableEntity && playerData.friendship != new_friendship) { - TameableEntity tamableEntity = (TameableEntity) entity; - if (new_friendship == 3 && !tamableEntity.isTamed()) { - tamableEntity.setOwner(player); - } else if (new_friendship == -3 && tamableEntity.isTamed()) { - tamableEntity.setTamed(false); - tamableEntity.setOwnerUuid(null); + // Tame best friends and un-tame worst enemies + if (entity instanceof TameableEntity && playerData.friendship != new_friendship) { + TameableEntity tamableEntity = (TameableEntity) entity; + if (new_friendship == 3 && !tamableEntity.isTamed()) { + tamableEntity.setOwner(player); + } else if (new_friendship == -3 && tamableEntity.isTamed()) { + tamableEntity.setTamed(false); + tamableEntity.setOwnerUuid(null); + } } - } - // Emit friendship particles - if (playerData.friendship != new_friendship) { - int friendDiff = new_friendship - playerData.friendship; - if (friendDiff > 0) { - // Heart particles - if (new_friendship == 3) { - ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, HEART_BIG_PARTICLE, 0.5, 10); - } else { - ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, HEART_SMALL_PARTICLE, 0.1, 1); - } + // Emit friendship particles + if (playerData.friendship != new_friendship) { + int friendDiff = new_friendship - playerData.friendship; + if (friendDiff > 0) { + // Heart particles + if (new_friendship == 3) { + ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, HEART_BIG_PARTICLE, 0.5, 10); + } else { + ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, HEART_SMALL_PARTICLE, 0.1, 1); + } - } else if (friendDiff < 0) { - // Fire particles - if (new_friendship == -3) { - ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, FIRE_BIG_PARTICLE, 0.5, 10); - } else { - ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, FIRE_SMALL_PARTICLE, 0.1, 1); + } else if (friendDiff < 0) { + // Fire particles + if (new_friendship == -3) { + ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, FIRE_BIG_PARTICLE, 0.5, 10); + } else { + ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, FIRE_SMALL_PARTICLE, 0.1, 1); + } } } + + playerData.friendship = new_friendship; } + } - playerData.friendship = new_friendship; + // Get cleaned message (i.e. no <BEHAVIOR> strings) + String cleanedMessage = result.getCleanedMessage(); + if (cleanedMessage.isEmpty()) { + cleanedMessage = Randomizer.getRandomMessage(Randomizer.RandomType.NO_RESPONSE); } - } - // Get cleaned message (i.e. no <BEHAVIOR> strings) - String cleanedMessage = result.getCleanedMessage(); - if (cleanedMessage.isEmpty()) { - cleanedMessage = Randomizer.getRandomMessage(Randomizer.RandomType.NO_RESPONSE); - } + // Add ASSISTANT message to history + this.addMessage(cleanedMessage, ChatDataManager.ChatSender.ASSISTANT, player, systemPrompt); - // Add ASSISTANT message to history - this.addMessage(cleanedMessage, ChatDataManager.ChatSender.ASSISTANT, player, systemPrompt); + // Update the last entry in previousMessages to use the original message + this.previousMessages.set(this.previousMessages.size() - 1, + new ChatMessage(result.getOriginalMessage(), ChatDataManager.ChatSender.ASSISTANT, player.getDisplayName().getString())); - // Update the last entry in previousMessages to use the original message - this.previousMessages.set(this.previousMessages.size() - 1, - new ChatMessage(result.getOriginalMessage(), ChatDataManager.ChatSender.ASSISTANT, player.getDisplayName().getString())); + } else { + // No valid LLM response + throw new RuntimeException(ChatGPTRequest.lastErrorMessage); + } + + } catch (Exception e) { + // Log the exception for debugging + LOGGER.error("Error processing LLM response", e); - } else { // Error / No Chat Message (Failure) String randomErrorMessage = Randomizer.getRandomMessage(Randomizer.RandomType.ERROR); this.addMessage(randomErrorMessage, ChatDataManager.ChatSender.ASSISTANT, player, systemPrompt); - // Determine error message to display - String errorMessage = "Help is available at discord.creaturechat.com"; - if (!ChatGPTRequest.lastErrorMessage.isEmpty()) { - errorMessage = "Error: " + truncateString(ChatGPTRequest.lastErrorMessage, 55) + "\n" + errorMessage; + // Remove the error message from history to prevent it from affecting future ChatGPT requests + if (!previousMessages.isEmpty()) { + previousMessages.remove(previousMessages.size() - 1); } - // Send clickable error message - ServerPackets.SendClickableError(player, - errorMessage, "http://discord.creaturechat.com"); - - // Clear history (if no character sheet was generated) - if (characterSheet.isEmpty()) { - previousMessages.clear(); + String errorMessage = "Error: "; + if (e.getMessage() != null && !e.getMessage().isEmpty()) { + errorMessage += truncateString(e.getMessage(), 55) + "\n"; } + errorMessage += "Help is available at discord.creaturechat.com"; + ServerPackets.SendClickableError(player, errorMessage, "http://discord.creaturechat.com"); } }); } -- libgit2 0.27.0