diff --git a/src/main/java/emu/grasscutter/game/managers/chat/ChatManager.java b/src/main/java/emu/grasscutter/game/managers/chat/ChatManager.java index d63592a85..d9d243dcd 100644 --- a/src/main/java/emu/grasscutter/game/managers/chat/ChatManager.java +++ b/src/main/java/emu/grasscutter/game/managers/chat/ChatManager.java @@ -1,19 +1,33 @@ package emu.grasscutter.game.managers.chat; +import emu.grasscutter.GameConstants; import emu.grasscutter.command.CommandMap; import emu.grasscutter.game.player.Player; -import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.packet.send.PacketPlayerChatNotify; import emu.grasscutter.server.packet.send.PacketPrivateChatNotify; +import emu.grasscutter.server.packet.send.PacketPullPrivateChatRsp; +import emu.grasscutter.server.packet.send.PacketPullRecentChatRsp; +import emu.grasscutter.utils.Utils; import java.util.regex.Pattern; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static emu.grasscutter.Configuration.*; public class ChatManager implements ChatManagerHandler { static final String PREFIXES = "[/!]"; static final Pattern RE_PREFIXES = Pattern.compile(PREFIXES); static final Pattern RE_COMMANDS = Pattern.compile("\n" + PREFIXES); + // We store the chat history for ongoing sessions in the form + // user id -> chat partner id -> [messages] + private final Map>> history = new HashMap<>(); + private final GameServer server; public ChatManager(GameServer server) { @@ -32,13 +46,93 @@ public class ChatManager implements ChatManagerHandler { return true; } - public void sendPrivateMessage(Player player, int targetUid, String message) { - // Sanity checks + /******************** + * Chat history handling + ********************/ + private void putInHistory(int uid, int targetId, ChatInfo info) { + if (!this.history.containsKey(uid)) { + this.history.put(uid, new HashMap<>()); + } + if (!this.history.get(uid).containsKey(targetId)) { + this.history.get(uid).put(targetId, new ArrayList<>()); + } + + this.history.get(uid).get(targetId).add(info); + } + + public void clearHistoryOnLogout(Player player) { + if (this.history.containsKey(player.getUid())) { + this.history.remove(player.getUid()); + } + } + + public void handlePullPrivateChatReq(Player player, int targetUid) { + if (this.history.containsKey(player.getUid()) && this.history.get(player.getUid()).containsKey(targetUid)) { + player.sendPacket(new PacketPullPrivateChatRsp(this.history.get(player.getUid()).get(targetUid))); + } + else { + player.sendPacket(new PacketPullPrivateChatRsp(List.of())); + } + } + + public void handlePullRecentChatReq(Player player) { + // For now, we send the list three messages from the server for the recent chat history. + // This matches the previous behavior, but ultimately, we should probably keep track of the last chat partner + // for every given player and return the last messages exchanged with that partner. + if (this.history.containsKey(player.getUid()) && this.history.get(player.getUid()).containsKey(GameConstants.SERVER_CONSOLE_UID)) { + int historyLength = this.history.get(player.getUid()).get(GameConstants.SERVER_CONSOLE_UID).size(); + var messages = this.history.get(player.getUid()).get(GameConstants.SERVER_CONSOLE_UID).subList(Math.max(historyLength - 3, 0), historyLength); + player.sendPacket(new PacketPullRecentChatRsp(messages)); + } + else { + player.sendPacket(new PacketPullRecentChatRsp(List.of())); + } + } + + /******************** + * Sending messages + ********************/ + public void sendPrivateMessageFromServer(int targetUid, String message) { + // Sanity checks. if (message == null || message.length() == 0) { return; } + + // Get target. + Player target = getServer().getPlayerByUid(targetUid); + if (target == null) { + return; + } - // Get target + // Create chat packet and put in history. + var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, message); + putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo()); + + // Send. + target.sendPacket(packet); + } + public void sendPrivateMessageFromServer(int targetUid, int emote) { + // Get target. + Player target = getServer().getPlayerByUid(targetUid); + if (target == null) { + return; + } + + // Create chat packet and put in history. + var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, emote); + putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo()); + + // Send. + target.sendPacket(packet); + } + + public void sendPrivateMessage(Player player, int targetUid, String message) { + // Sanity checks. + if (message == null || message.length() == 0) { + return; + } + + // Get target. Player target = getServer().getPlayerByUid(targetUid); // Check if command @@ -46,30 +140,42 @@ public class ChatManager implements ChatManagerHandler { return; } - if (target == null) { + if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) { return; } - // Create chat packet - BasePacket packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), message); + // Create chat packet. + var packet = new PacketPrivateChatNotify(player.getUid(), targetUid, message); + // Send and put in history. player.sendPacket(packet); - target.sendPacket(packet); + putInHistory(player.getUid(), targetUid, packet.getChatInfo()); + + if (target != null) { + target.sendPacket(packet); + putInHistory(targetUid, player.getUid(), packet.getChatInfo()); + } } public void sendPrivateMessage(Player player, int targetUid, int emote) { - // Get target + // Get target. Player target = getServer().getPlayerByUid(targetUid); - - if (target == null) { + + if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) { return; } + + // Create chat packet. + var packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), emote); - // Create chat packet - BasePacket packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), emote); - + // Send and put is history. player.sendPacket(packet); - target.sendPacket(packet); + putInHistory(player.getUid(), targetUid, packet.getChatInfo()); + + if (target != null) { + target.sendPacket(packet); + putInHistory(targetUid, player.getUid(), packet.getChatInfo()); + } } public void sendTeamMessage(Player player, int channel, String message) { @@ -86,9 +192,25 @@ public class ChatManager implements ChatManagerHandler { // Create and send chat packet player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, message)); } - public void sendTeamMessage(Player player, int channel, int icon) { // Create and send chat packet player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, icon)); } + + /******************** + * Welcome messages + ********************/ + public void sendServerWelcomeMessages(Player player) { + var joinOptions = GAME_INFO.joinOptions; + + if (joinOptions.welcomeEmotes != null && joinOptions.welcomeEmotes.length > 0) { + this.sendPrivateMessageFromServer(player.getUid(), joinOptions.welcomeEmotes[Utils.randomRange(0, joinOptions.welcomeEmotes.length - 1)]); + } + + if (joinOptions.welcomeMessage != null && joinOptions.welcomeMessage.length() > 0) { + this.sendPrivateMessageFromServer(player.getUid(), joinOptions.welcomeMessage); + } + + this.sendPrivateMessageFromServer(player.getUid(), "THIS IS AN EXPERIMENTAL BUILD OF GRASSCUTTER FOR 2.7.50/2.8\nDON'T LEAK <3"); + } } diff --git a/src/main/java/emu/grasscutter/game/managers/chat/ChatManagerHandler.java b/src/main/java/emu/grasscutter/game/managers/chat/ChatManagerHandler.java index 013b43e47..b8b197a2f 100644 --- a/src/main/java/emu/grasscutter/game/managers/chat/ChatManagerHandler.java +++ b/src/main/java/emu/grasscutter/game/managers/chat/ChatManagerHandler.java @@ -9,4 +9,10 @@ public interface ChatManagerHandler { void sendPrivateMessage(Player player, int targetUid, int emote); void sendTeamMessage(Player player, int channel, String message); void sendTeamMessage(Player player, int channel, int icon); + void sendPrivateMessageFromServer(int targetUid, String message); + void sendPrivateMessageFromServer(int targetUid, int emote); + void handlePullPrivateChatReq(Player player, int targetUid); + void clearHistoryOnLogout(Player player); + void sendServerWelcomeMessages(Player player); + void handlePullRecentChatReq(Player player); } diff --git a/src/main/java/emu/grasscutter/game/player/Player.java b/src/main/java/emu/grasscutter/game/player/Player.java index ec101ea83..a2d23a26f 100644 --- a/src/main/java/emu/grasscutter/game/player/Player.java +++ b/src/main/java/emu/grasscutter/game/player/Player.java @@ -1052,7 +1052,9 @@ public class Player { this.messageHandler.append(message.toString()); return; } - this.sendPacket(new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, getUid(), message.toString())); + + this.getServer().getChatManager().sendPrivateMessageFromServer(getUid(), message.toString()); + // this.sendPacket(new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, getUid(), message.toString())); } /** @@ -1062,7 +1064,8 @@ public class Player { * @param message The message to send. */ public void sendMessage(Player sender, Object message) { - this.sendPacket(new PacketPrivateChatNotify(sender.getUid(), this.getUid(), message.toString())); + // this.sendPacket(new PacketPrivateChatNotify(sender.getUid(), this.getUid(), message.toString())); + this.getServer().getChatManager().sendPrivateMessage(sender, this.getUid(), message.toString()); } // ---------------------MAIL------------------------ @@ -1541,6 +1544,9 @@ public class Player { // First notify packets sent this.setHasSentAvatarDataNotify(true); + // Send server welcome chat. + this.getServer().getChatManager().sendServerWelcomeMessages(this); + // Set session state session.setState(SessionState.ACTIVE); @@ -1558,6 +1564,9 @@ public class Player { public void onLogout() { try{ + // Clear chat history. + this.getServer().getChatManager().clearHistoryOnLogout(this); + // stop stamina calculation getStaminaManager().stopSustainedStaminaHandler(); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullPrivateChatReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullPrivateChatReq.java index b00e7ad12..c579d20b0 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullPrivateChatReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullPrivateChatReq.java @@ -13,8 +13,10 @@ public class HandlerPullPrivateChatReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { PullPrivateChatReq req = PullPrivateChatReq.parseFrom(payload); + + session.getServer().getChatManager().handlePullPrivateChatReq(session.getPlayer(), req.getTargetUid()); - session.send(new PacketPullPrivateChatRsp()); + // session.send(new PacketPullPrivateChatRsp(req.getTargetUid())); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullRecentChatReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullRecentChatReq.java index 3a7d8262d..fa5e6ba8a 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullRecentChatReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullRecentChatReq.java @@ -10,6 +10,6 @@ import emu.grasscutter.server.packet.send.PacketPullRecentChatRsp; public class HandlerPullRecentChatReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - session.send(new PacketPullRecentChatRsp(session.getPlayer())); + session.getServer().getChatManager().handlePullRecentChatReq(session.getPlayer()); } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPrivateChatNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPrivateChatNotify.java index 94f389521..6454b497c 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPrivateChatNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPrivateChatNotify.java @@ -6,6 +6,8 @@ import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo; import emu.grasscutter.net.proto.PrivateChatNotifyOuterClass.PrivateChatNotify; public class PacketPrivateChatNotify extends BasePacket { + private ChatInfo info; + public PacketPrivateChatNotify(int senderId, int recvId, String message) { super(PacketOpcodes.PrivateChatNotify); @@ -15,7 +17,8 @@ public class PacketPrivateChatNotify extends BasePacket { .setToUid(recvId) .setText(message) .build(); - + this.info = info; + PrivateChatNotify proto = PrivateChatNotify.newBuilder() .setChatInfo(info) .build(); @@ -32,6 +35,7 @@ public class PacketPrivateChatNotify extends BasePacket { .setToUid(recvId) .setIcon(emote) .build(); + this.info = info; PrivateChatNotify proto = PrivateChatNotify.newBuilder() .setChatInfo(info) @@ -39,4 +43,8 @@ public class PacketPrivateChatNotify extends BasePacket { this.setData(proto); } + + public ChatInfo getChatInfo() { + return this.info; + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPullPrivateChatRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPullPrivateChatRsp.java index 094a1e9c1..e7f52a5af 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPullPrivateChatRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPullPrivateChatRsp.java @@ -1,16 +1,29 @@ package emu.grasscutter.server.packet.send; +import java.util.List; + import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo; import emu.grasscutter.net.proto.PullPrivateChatRspOuterClass.PullPrivateChatRsp; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; public class PacketPullPrivateChatRsp extends BasePacket { - public PacketPullPrivateChatRsp() { + public PacketPullPrivateChatRsp(List history) { super(PacketOpcodes.PullPrivateChatRsp); - PullPrivateChatRsp proto = PullPrivateChatRsp.newBuilder().build(); + PullPrivateChatRsp.Builder builder = PullPrivateChatRsp.newBuilder(); + + if (history == null) { + builder.setRetcode(Retcode.RET_FAIL_VALUE); + } + else { + for (var info : history) { + builder.addChatInfo(info); + } + } - this.setData(proto); + this.setData(builder.build()); } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java index 0e757d11b..472075c76 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java @@ -10,33 +10,14 @@ import emu.grasscutter.utils.Utils; import static emu.grasscutter.Configuration.*; +import java.util.List; + public class PacketPullRecentChatRsp extends BasePacket { - public PacketPullRecentChatRsp(Player player) { + public PacketPullRecentChatRsp(List messages) { super(PacketOpcodes.PullRecentChatRsp); - var joinOptions = GAME_INFO.joinOptions; - PullRecentChatRsp.Builder proto = PullRecentChatRsp.newBuilder(); - - if (joinOptions.welcomeEmotes != null && joinOptions.welcomeEmotes.length > 0) { - ChatInfo welcomeEmote = ChatInfo.newBuilder() - .setTime((int) (System.currentTimeMillis() / 1000)) - .setUid(GameConstants.SERVER_CONSOLE_UID) - .setToUid(player.getUid()) - .setIcon(joinOptions.welcomeEmotes[Utils.randomRange(0, joinOptions.welcomeEmotes.length - 1)]) - .build(); - - proto.addChatInfo(welcomeEmote); - } - - if (joinOptions.welcomeMessage != null && joinOptions.welcomeMessage.length() > 0) { - ChatInfo welcomeMessage = ChatInfo.newBuilder() - .setTime((int) (System.currentTimeMillis() / 1000)) - .setUid(GameConstants.SERVER_CONSOLE_UID) - .setToUid(player.getUid()) - .setText(joinOptions.welcomeMessage) - .build(); - proto.addChatInfo(welcomeMessage); - } + PullRecentChatRsp.Builder proto = PullRecentChatRsp.newBuilder() + .addAllChatInfo(messages); this.setData(proto); }