using AscNet.Common.MsgPack; using AscNet.Common.Util; using AscNet.GameServer.Commands; using AscNet.Table.V2.share.chat; using MessagePack; namespace AscNet.GameServer.Handlers { #region MsgPackScheme #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. [MessagePackObject(true)] public class SelectChatChannelRequest { public int ChannelId { get; set; } } [MessagePackObject(true)] public class SelectChatChannelResponse { public int Code { get; set; } } [MessagePackObject(true)] public class GetEmojiPackageIdResponse { public int Code { get; set; } public List OrderEmojiPackageIds { get; set; } = new(); } [MessagePackObject(true)] public class SendChatRequest { public ChatData ChatData { get; set; } public List TargetIdList { get; set; } = new(); } [MessagePackObject(true)] public class SendChatResponse { public int Code { get; set; } public ChatData ChatData { get; set; } public long RefreshTime { get; set; } } [MessagePackObject(true)] public class NotifyWorldChat { public List ChatMessages { get; set; } = new(); } [MessagePackObject(true)] public class ChatData { public int MessageId { get; set; } public ChatChannelType ChannelType { get; set; } public ChatMsgType MsgType { get; set; } public long SenderId { get; set; } public int Icon { get; set; } public string NickName { get; set; } public long TargetId { get; set; } public long CreateTime { get; set; } public string? Content { get; set; } public int GiftId { get; set; } public int GiftCount { get; set; } public ChatGiftState GiftStatus { get; set; } public bool IsRead { get; set; } public int CurrMedalId { get; set; } public int BabelTowerLevel { get; set; } public int BabelTowerTitleId { get; set; } public int GuildRankLevel { get; set; } public string? GuildName { get; set; } public int CollectWordId { get; set; } public int NameplateId { get; set; } } public enum ChatChannelType { System = 1, World = 2, Private = 3, Room = 4, Battle = 5, Guild = 6, Mentor = 7, } public enum ChatMsgType { Normal = 1, Emoji = 2, Gift = 3, Tips = 4, RoomMsg = 5, System = 6, SpringFestival = 7, } public enum ChatGiftState { None = 0, WaitReceive = 1, Received = 2, Fetched = 3, } #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. #endregion internal class ChatModule { [RequestPacketHandler("EnterWorldChatRequest")] public static void EnterWorldChatRequestHandler(Session session, Packet.Request packet) { EnterWorldChatResponse enterWorldChatResponse = new() { Code = 0, ChannelId = 0 }; session.SendResponse(enterWorldChatResponse, packet.Id); } [RequestPacketHandler("GetWorldChannelInfoRequest")] public static void GetWorldChannelInfoRequestHandler(Session session, Packet.Request packet) { GetWorldChannelInfoResponse getWorldChannelInfoResponse = new(); getWorldChannelInfoResponse.ChannelInfos.Add(new() { ChannelId = 0, // Channel 1 PlayerNum = 0 }); getWorldChannelInfoResponse.ChannelInfos.Add(new() { ChannelId = 1, // Recuruit channel (we don't use this!) PlayerNum = 0 }); session.SendResponse(getWorldChannelInfoResponse, packet.Id); } [RequestPacketHandler("OfflineMessageRequest")] public static void OfflineMessageRequestHandler(Session session, Packet.Request packet) { OfflineMessageResponse offlineMessageResponse = new() { Code = 0, Messages = Array.Empty() }; session.SendResponse(offlineMessageResponse, packet.Id); } [RequestPacketHandler("SendChatRequest")] public static void SendChatRequestHandler(Session session, Packet.Request packet) { SendChatRequest request = MessagePackSerializer.Deserialize(packet.Content); request.ChatData.SenderId = session.player.PlayerData.Id; request.ChatData.Icon = (int)session.player.PlayerData.CurrHeadPortraitId; request.ChatData.NickName = session.player.PlayerData.Name; NotifyWorldChat notifyWorldChat = new(); notifyWorldChat.ChatMessages.Add(request.ChatData); if (request.ChatData.Content is not null && request.ChatData.Content.StartsWith('/')) { var cmdStrings = request.ChatData.Content.Split(" "); try { Command? cmd = CommandFactory.CreateCommand(cmdStrings.First().Split('/').Last(), session, cmdStrings[1..]); if (cmd is null) { notifyWorldChat.ChatMessages.Add(MakeLuciaMessage($"Invalid command {cmdStrings.First().Split('/').Last()}, try /help")); } cmd?.Execute(); notifyWorldChat.ChatMessages.Add(MakeLuciaMessage("Command executed!")); } catch (CommandMessageCallbackException ex) { notifyWorldChat.ChatMessages.Add(MakeLuciaMessage(ex.Message)); } catch (Exception ex) { #if DEBUG notifyWorldChat.ChatMessages.Add(MakeLuciaMessage($"Command {cmdStrings.First().Split('/').Last()} failed to execute!, " + ex.ToString())); #else notifyWorldChat.ChatMessages.Add(MakeLuciaMessage($"Command {cmdStrings.First().Split('/').Last()} failed to execute!, " + ex.Message)); #endif } } session.SendPush(notifyWorldChat); session.SendResponse(new SendChatResponse() { Code = 0, ChatData = request.ChatData, RefreshTime = DateTimeOffset.Now.ToUnixTimeSeconds() - 10 }, packet.Id); } [RequestPacketHandler("SelectChatChannelRequest")] public static void SelectChatChannelRequestHandler(Session session, Packet.Request packet) { // SelectChatChannelRequest request = MessagePackSerializer.Deserialize(packet.Content); // disabling channel switching because the game is cringe and we don't need it anyway. session.SendResponse(new SelectChatChannelResponse() { Code = 20033013 // ChatChannelNotExist }, packet.Id); } public static ChatData MakeLuciaMessage(string content) { return new ChatData() { Content = content, Icon = 9010102, ChannelType = ChatChannelType.World, MsgType = ChatMsgType.Normal, NickName = "System - Lucia" }; } #region EmojiPackModule [RequestPacketHandler("GetEmojiPackageIdRequest")] public static void GetEmojiPackageIdRequestHandler(Session session, Packet.Request packet) { session.SendResponse(new GetEmojiPackageIdResponse() { Code = 0, OrderEmojiPackageIds = TableReaderV2.Parse().Select(x => x.Id).ToList() }, packet.Id); } #endregion } }