Commit 56459d1f by Jonathan Thomas

- Move animation frame code, so it works with multiple entities at the same time

- Add new packet messages for Open UI, Close UI, and Sync UI status to the client
- Render ... animation above player heads who have the Chat UI open
- Added removed() method to Chat UI screen, so it always gets called, even when a player logs off
- Chat screen now sends messages to server with open/close status
parent f67f7ad0
Pipeline #12008 passed with stage
in 21 seconds
...@@ -44,12 +44,19 @@ public class ClientPackets { ...@@ -44,12 +44,19 @@ public class ClientPackets {
ClientPlayNetworking.send(ServerPackets.PACKET_C2S_READ_NEXT, buf); ClientPlayNetworking.send(ServerPackets.PACKET_C2S_READ_NEXT, buf);
} }
public static void sendStartChat(Entity entity) { public static void sendOpenChat(Entity entity) {
PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer()); PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
buf.writeString(entity.getUuidAsString()); buf.writeString(entity.getUuidAsString());
// Send C2S packet // Send C2S packet
ClientPlayNetworking.send(ServerPackets.PACKET_C2S_START_CHAT, buf); ClientPlayNetworking.send(ServerPackets.PACKET_C2S_OPEN_CHAT, buf);
}
public static void sendCloseChat() {
PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
// Send C2S packet
ClientPlayNetworking.send(ServerPackets.PACKET_C2S_CLOSE_CHAT, buf);
} }
public static void setChatStatus(Entity entity, ChatDataManager.ChatStatus new_status) { public static void setChatStatus(Entity entity, ChatDataManager.ChatStatus new_status) {
...@@ -142,6 +149,23 @@ public class ClientPackets { ...@@ -142,6 +149,23 @@ public class ClientPackets {
} }
}); });
}); });
// Client-side packet handler, player status sync
ClientPlayNetworking.registerGlobalReceiver(ServerPackets.PACKET_S2C_PLAYER_STATUS, (client, handler, buffer, responseSender) -> {
// Read the data from the server packet
UUID playerId = UUID.fromString(buffer.readString());
boolean isChatOpen = buffer.readBoolean();
// Update the player status data manager on the client-side
client.execute(() -> { // Make sure to run on the client thread
if (isChatOpen) {
PlayerMessageManager.openChatUI(playerId);
} else {
PlayerMessageManager.closeChatUI(playerId);
}
});
});
} }
} }
...@@ -406,15 +406,6 @@ public class BubbleRenderer { ...@@ -406,15 +406,6 @@ public class BubbleRenderer {
// Draw 'pending' button // Draw 'pending' button
drawIcon("button-dot-" + animationFrame, matrices, -16, textHeaderHeight, 32, 17); drawIcon("button-dot-" + animationFrame, matrices, -16, textHeaderHeight, 32, 17);
// Calculate animation frames (0-8) every X ticks
if (lastTick != tick && tick % 5 == 0) {
lastTick = tick;
animationFrame++;
}
if (animationFrame > 8) {
animationFrame = 0;
}
} else if (chatData.sender == ChatDataManager.ChatSender.ASSISTANT && chatData.status != ChatDataManager.ChatStatus.HIDDEN) { } else if (chatData.sender == ChatDataManager.ChatSender.ASSISTANT && chatData.status != ChatDataManager.ChatStatus.HIDDEN) {
// Draw Entity (Custom Name) // Draw Entity (Custom Name)
drawEntityName(entity, matrix, immediate, fullBright, 24F + DISPLAY_PADDING); drawEntityName(entity, matrix, immediate, fullBright, 24F + DISPLAY_PADDING);
...@@ -471,6 +462,20 @@ public class BubbleRenderer { ...@@ -471,6 +462,20 @@ public class BubbleRenderer {
// Draw Player Name // Draw Player Name
drawEntityName(entity, matrices.peek().getPositionMatrix(), immediate, fullBright, 24F + DISPLAY_PADDING); drawEntityName(entity, matrices.peek().getPositionMatrix(), immediate, fullBright, 24F + DISPLAY_PADDING);
if (PlayerMessageManager.isChatUIOpen(entity.getUuid())) {
// Draw 'pending' button (when Chat UI is open)
drawIcon("button-dot-" + animationFrame, matrices, -16, textHeaderHeight, 32, 17);
}
}
// Calculate animation frames (0-8) every X ticks
if (lastTick != tick && tick % 5 == 0) {
lastTick = tick;
animationFrame++;
}
if (animationFrame > 8) {
animationFrame = 0;
} }
// Pop the matrix to return to the original state. // Pop the matrix to return to the original state.
......
...@@ -8,6 +8,7 @@ import net.minecraft.client.gui.screen.Screen; ...@@ -8,6 +8,7 @@ import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.TextFieldWidget; import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.sound.SoundEvents; import net.minecraft.sound.SoundEvents;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
...@@ -23,9 +24,12 @@ public class ChatScreen extends Screen { ...@@ -23,9 +24,12 @@ public class ChatScreen extends Screen {
private Entity screenEntity; private Entity screenEntity;
private final Text labelText = Text.literal("Enter your message:"); private final Text labelText = Text.literal("Enter your message:");
public ChatScreen(Entity entity) { public ChatScreen(Entity entity, PlayerEntity player) {
super(Text.literal("Simple Chat")); super(Text.literal("Simple Chat"));
screenEntity = entity; screenEntity = entity;
// Notify server that chat screen
ClientPackets.sendOpenChat(entity);
} }
@Override @Override
...@@ -121,4 +125,12 @@ public class ChatScreen extends Screen { ...@@ -121,4 +125,12 @@ public class ChatScreen extends Screen {
// Return false to prevent the game from pausing when the screen is open // Return false to prevent the game from pausing when the screen is open
return false; return false;
} }
@Override
public void removed() {
super.removed();
// Notify server that chat screen
ClientPackets.sendCloseChat();
}
} }
...@@ -117,8 +117,7 @@ public class ClickHandler { ...@@ -117,8 +117,7 @@ public class ClickHandler {
ClientPackets.sendUpdateLineNumber(closestEntity, chatData.currentLineNumber - ChatDataManager.DISPLAY_NUM_LINES); ClientPackets.sendUpdateLineNumber(closestEntity, chatData.currentLineNumber - ChatDataManager.DISPLAY_NUM_LINES);
} else if (hitRegion.equals("RIGHT") && chatData.isEndOfMessage()) { } else if (hitRegion.equals("RIGHT") && chatData.isEndOfMessage()) {
// End of chat (open player chat screen) // End of chat (open player chat screen)
ClientPackets.sendStartChat(closestEntity); client.setScreen(new ChatScreen(closestEntity, client.player));
client.setScreen(new ChatScreen(closestEntity));
} else if (hitRegion.equals("TOP")) { } else if (hitRegion.equals("TOP")) {
// Hide chat // Hide chat
ClientPackets.setChatStatus(closestEntity, ChatDataManager.ChatStatus.HIDDEN); ClientPackets.setChatStatus(closestEntity, ChatDataManager.ChatStatus.HIDDEN);
......
...@@ -10,6 +10,7 @@ import java.util.concurrent.ConcurrentHashMap; ...@@ -10,6 +10,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/ */
public class PlayerMessageManager { public class PlayerMessageManager {
private static final ConcurrentHashMap<UUID, PlayerMessage> messages = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<UUID, PlayerMessage> messages = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<UUID, Boolean> openChatUIs = new ConcurrentHashMap<>();
public static void addMessage(UUID playerUUID, String messageText, int ticks) { public static void addMessage(UUID playerUUID, String messageText, int ticks) {
messages.put(playerUUID, new PlayerMessage(playerUUID.toString(), messageText, ticks)); messages.put(playerUUID, new PlayerMessage(playerUUID.toString(), messageText, ticks));
...@@ -38,5 +39,18 @@ public class PlayerMessageManager { ...@@ -38,5 +39,18 @@ public class PlayerMessageManager {
messages.remove(uuid); messages.remove(uuid);
} }
} }
// Methods for managing open chat UIs
public static void openChatUI(UUID playerId) {
openChatUIs.put(playerId, Boolean.TRUE);
}
public static boolean isChatUIOpen(UUID playerId) {
return openChatUIs.getOrDefault(playerId, Boolean.FALSE);
}
public static void closeChatUI(UUID playerId) {
openChatUIs.remove(playerId);
}
} }
...@@ -16,6 +16,7 @@ import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; ...@@ -16,6 +16,7 @@ import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.PacketByteBuf; import net.minecraft.network.PacketByteBuf;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
...@@ -41,10 +42,12 @@ public class ServerPackets { ...@@ -41,10 +42,12 @@ public class ServerPackets {
public static final Identifier PACKET_C2S_GREETING = new Identifier("creaturechat", "packet_c2s_greeting"); public static final Identifier PACKET_C2S_GREETING = new Identifier("creaturechat", "packet_c2s_greeting");
public static final Identifier PACKET_C2S_READ_NEXT = new Identifier("creaturechat", "packet_c2s_read_next"); public static final Identifier PACKET_C2S_READ_NEXT = new Identifier("creaturechat", "packet_c2s_read_next");
public static final Identifier PACKET_C2S_SET_STATUS = new Identifier("creaturechat", "packet_c2s_set_status"); public static final Identifier PACKET_C2S_SET_STATUS = new Identifier("creaturechat", "packet_c2s_set_status");
public static final Identifier PACKET_C2S_START_CHAT = new Identifier("creaturechat", "packet_c2s_start_chat"); public static final Identifier PACKET_C2S_OPEN_CHAT = new Identifier("creaturechat", "packet_c2s_open_chat");
public static final Identifier PACKET_C2S_CLOSE_CHAT = new Identifier("creaturechat", "packet_c2s_close_chat");
public static final Identifier PACKET_C2S_SEND_CHAT = new Identifier("creaturechat", "packet_c2s_send_chat"); public static final Identifier PACKET_C2S_SEND_CHAT = new Identifier("creaturechat", "packet_c2s_send_chat");
public static final Identifier PACKET_S2C_MESSAGE = new Identifier("creaturechat", "packet_s2c_message"); public static final Identifier PACKET_S2C_MESSAGE = new Identifier("creaturechat", "packet_s2c_message");
public static final Identifier PACKET_S2C_LOGIN = new Identifier("creaturechat", "packet_s2c_login"); public static final Identifier PACKET_S2C_LOGIN = new Identifier("creaturechat", "packet_s2c_login");
public static final Identifier PACKET_S2C_PLAYER_STATUS = new Identifier("creaturechat", "packet_s2c_player_status");
public static void register() { public static void register() {
// Handle packet for Greeting // Handle packet for Greeting
...@@ -103,8 +106,8 @@ public class ServerPackets { ...@@ -103,8 +106,8 @@ public class ServerPackets {
}); });
}); });
// Handle packet for Start Chat // Handle packet for Open Chat
ServerPlayNetworking.registerGlobalReceiver(PACKET_C2S_START_CHAT, (server, player, handler, buf, responseSender) -> { ServerPlayNetworking.registerGlobalReceiver(PACKET_C2S_OPEN_CHAT, (server, player, handler, buf, responseSender) -> {
UUID entityId = UUID.fromString(buf.readString()); UUID entityId = UUID.fromString(buf.readString());
// Ensure that the task is synced with the server thread // Ensure that the task is synced with the server thread
...@@ -115,6 +118,18 @@ public class ServerPackets { ...@@ -115,6 +118,18 @@ public class ServerPackets {
TalkPlayerGoal talkGoal = new TalkPlayerGoal(player, entity, 7F); TalkPlayerGoal talkGoal = new TalkPlayerGoal(player, entity, 7F);
EntityBehaviorManager.addGoal(entity, talkGoal, GoalPriority.TALK_PLAYER); EntityBehaviorManager.addGoal(entity, talkGoal, GoalPriority.TALK_PLAYER);
} }
// Sync player UI status to all clients
BroadcastPlayerStatus(player, true);
});
});
// Handle packet for Close Chat
ServerPlayNetworking.registerGlobalReceiver(PACKET_C2S_CLOSE_CHAT, (server, player, handler, buf, responseSender) -> {
server.execute(() -> {
// Sync player UI status to all clients
BroadcastPlayerStatus(player, false);
}); });
}); });
...@@ -255,7 +270,7 @@ public class ServerPackets { ...@@ -255,7 +270,7 @@ public class ServerPackets {
// Set custom name (if null) // Set custom name (if null)
String characterName = chatData.getCharacterProp("name"); String characterName = chatData.getCharacterProp("name");
if (!characterName.isEmpty() && !characterName.equals("N/A") && entity.getCustomName() == null) { if (!characterName.isEmpty() && !characterName.equals("N/A") && entity.getCustomName() == null) {
LOGGER.info("Setting entity name to " + characterName + " for " + chatData.entityId); LOGGER.debug("Setting entity name to " + characterName + " for " + chatData.entityId);
entity.setCustomName(Text.literal(characterName)); entity.setCustomName(Text.literal(characterName));
entity.setCustomNameVisible(true); entity.setCustomNameVisible(true);
} }
...@@ -273,11 +288,26 @@ public class ServerPackets { ...@@ -273,11 +288,26 @@ public class ServerPackets {
// Iterate over all players and send the packet // Iterate over all players and send the packet
for (ServerPlayerEntity player : serverInstance.getPlayerManager().getPlayerList()) { for (ServerPlayerEntity player : serverInstance.getPlayerManager().getPlayerList()) {
LOGGER.info("Server broadcast message to client: " + player.getName().getString() + " | Message: " + chatData.currentMessage); LOGGER.debug("Server broadcast message to client: " + player.getName().getString() + " | Message: " + chatData.currentMessage);
ServerPlayNetworking.send(player, PACKET_S2C_MESSAGE, buffer); ServerPlayNetworking.send(player, PACKET_S2C_MESSAGE, buffer);
} }
break; break;
} }
} }
} }
// Send new message to all connected players
public static void BroadcastPlayerStatus(PlayerEntity player, boolean isChatOpen) {
PacketByteBuf buffer = new PacketByteBuf(Unpooled.buffer());
// Write the entity's chat updated data
buffer.writeString(player.getUuidAsString());
buffer.writeBoolean(isChatOpen);
// Iterate over all players and send the packet
for (ServerPlayerEntity serverPlayer : serverInstance.getPlayerManager().getPlayerList()) {
LOGGER.debug("Server broadcast " + player.getName().getString() + " player status to client: " + serverPlayer.getName().getString() + " | isChatOpen: " + isChatOpen);
ServerPlayNetworking.send(serverPlayer, PACKET_S2C_PLAYER_STATUS, buffer);
}
}
} }
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