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 {
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());
buf.writeString(entity.getUuidAsString());
// 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) {
......@@ -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 {
// Draw 'pending' button
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) {
// Draw Entity (Custom Name)
drawEntityName(entity, matrix, immediate, fullBright, 24F + DISPLAY_PADDING);
......@@ -471,6 +462,20 @@ public class BubbleRenderer {
// Draw Player Name
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.
......
......@@ -8,6 +8,7 @@ import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.Text;
import org.lwjgl.glfw.GLFW;
......@@ -23,9 +24,12 @@ public class ChatScreen extends Screen {
private Entity screenEntity;
private final Text labelText = Text.literal("Enter your message:");
public ChatScreen(Entity entity) {
public ChatScreen(Entity entity, PlayerEntity player) {
super(Text.literal("Simple Chat"));
screenEntity = entity;
// Notify server that chat screen
ClientPackets.sendOpenChat(entity);
}
@Override
......@@ -121,4 +125,12 @@ public class ChatScreen extends Screen {
// Return false to prevent the game from pausing when the screen is open
return false;
}
@Override
public void removed() {
super.removed();
// Notify server that chat screen
ClientPackets.sendCloseChat();
}
}
......@@ -117,8 +117,7 @@ public class ClickHandler {
ClientPackets.sendUpdateLineNumber(closestEntity, chatData.currentLineNumber - ChatDataManager.DISPLAY_NUM_LINES);
} else if (hitRegion.equals("RIGHT") && chatData.isEndOfMessage()) {
// End of chat (open player chat screen)
ClientPackets.sendStartChat(closestEntity);
client.setScreen(new ChatScreen(closestEntity));
client.setScreen(new ChatScreen(closestEntity, client.player));
} else if (hitRegion.equals("TOP")) {
// Hide chat
ClientPackets.setChatStatus(closestEntity, ChatDataManager.ChatStatus.HIDDEN);
......
......@@ -10,6 +10,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public class PlayerMessageManager {
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) {
messages.put(playerUUID, new PlayerMessage(playerUUID.toString(), messageText, ticks));
......@@ -38,5 +39,18 @@ public class PlayerMessageManager {
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;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.entity.Entity;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
......@@ -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_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_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_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_PLAYER_STATUS = new Identifier("creaturechat", "packet_s2c_player_status");
public static void register() {
// Handle packet for Greeting
......@@ -103,8 +106,8 @@ public class ServerPackets {
});
});
// Handle packet for Start Chat
ServerPlayNetworking.registerGlobalReceiver(PACKET_C2S_START_CHAT, (server, player, handler, buf, responseSender) -> {
// Handle packet for Open Chat
ServerPlayNetworking.registerGlobalReceiver(PACKET_C2S_OPEN_CHAT, (server, player, handler, buf, responseSender) -> {
UUID entityId = UUID.fromString(buf.readString());
// Ensure that the task is synced with the server thread
......@@ -115,6 +118,18 @@ public class ServerPackets {
TalkPlayerGoal talkGoal = new TalkPlayerGoal(player, entity, 7F);
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 {
// Set custom name (if null)
String characterName = chatData.getCharacterProp("name");
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.setCustomNameVisible(true);
}
......@@ -273,11 +288,26 @@ public class ServerPackets {
// Iterate over all players and send the packet
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);
}
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