Browse Source

Add chat message (#14)

* 添加readme

* 添加在client非1.12版本下,LoginDisconnect提示

* :pencil: Update README.

* :pencil: Update README.

* :pencil: Update README.

* :arrow_forward: Add bat file.

* :arrow_forward: Add bat file.

* :pencil: Fix typo.

* :pencil: Update README.

* Add new line

* Remove useless comments

* 'ver' to 'version'

* Correct the type of reason in LoginDisconnect.

* ver -> version

* Add server settings

* Fix a mistake in IServerSettings

* Fix the mistake of duplicate packet 0x00

* Read json from file without test

* Add all setting  entries in file

* Test settings grain

* Catch cluster connection failure exception

* Fix a bug in program.cs

* Apply changes requested

* Add reference 'Microsoft.Extensions.Logging'

* Add ChatMessage class and dispatch ChatMessage

* Move JSON conversion from router to session

* Transfer string to GameSession

String is transferred to GameSession instead of packet.

* Basic chat function

Some code needs to be modified.

* Improve the code of chat message
WangJun 8 years ago
parent
commit
6aca7a1ff4

+ 41 - 0
src/MineCase.Protocol/Protocol/Play/ChatMessage.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MineCase.Formats;
+using MineCase.Serialization;
+
+namespace MineCase.Protocol.Play
+{
+    [Packet(0x03)]
+    public sealed class ServerboundChatMessage
+    {
+        [SerializeAs(DataType.String)]
+        public string Message;
+
+        public static ServerboundChatMessage Deserialize(BinaryReader br)
+        {
+            return new ServerboundChatMessage
+            {
+                Message = br.ReadAsString()
+            };
+        }
+    }
+
+    // TODO
+    [Packet(0x0F)]
+    public sealed class ClientboundChatMessage : ISerializablePacket
+    {
+        [SerializeAs(DataType.Chat)]
+        public Chat JSONData;
+
+        [SerializeAs(DataType.Byte)]
+        public byte Position; // 0: chat (chat box), 1: system message (chat box), 2: game info (above hotbar).
+
+        public void Serialize(BinaryWriter bw)
+        {
+            bw.WriteAsChat(JSONData);
+            bw.WriteAsByte(Position);
+        }
+    }
+}

+ 7 - 0
src/MineCase.Protocol/Serialization/BinaryReaderExtensions.cs

@@ -4,6 +4,7 @@ using System.IO;
 using System.Runtime.CompilerServices;
 using System.Text;
 using System.Threading.Tasks;
+using MineCase.Formats;
 
 namespace MineCase.Serialization
 {
@@ -44,6 +45,12 @@ namespace MineCase.Serialization
             return Encoding.UTF8.GetString(bytes);
         }
 
+        public static Chat ReadAsChat(this BinaryReader br)
+        {
+            string str = br.ReadAsString();
+            return Chat.Parse(str);
+        }
+
         public static short ReadAsShort(this BinaryReader br) =>
             (short)br.ReadAsUnsignedShort();
 

+ 6 - 0
src/MineCase.Protocol/Serialization/BinaryWriterExtensions.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.IO;
 using System.Runtime.CompilerServices;
 using System.Text;
+using MineCase.Formats;
 using MineCase.Protocol;
 
 namespace MineCase.Serialization
@@ -42,6 +43,11 @@ namespace MineCase.Serialization
             bw.Write(bytes);
         }
 
+        public static void WriteAsChat(this BinaryWriter bw, Chat value)
+        {
+            bw.WriteAsString(value.ToString());
+        }
+
         public static void WriteAsShort(this BinaryWriter bw, short value)
         {
             bw.Write(((ushort)value).ToBigEndian());

+ 50 - 0
src/MineCase.Server.Grains/Game/GameSession.cs

@@ -3,6 +3,8 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using MineCase.Formats;
+using MineCase.Protocol.Play;
 using MineCase.Server.Network.Play;
 using MineCase.Server.User;
 using MineCase.Server.World;
@@ -53,6 +55,35 @@ namespace MineCase.Server.Game
             return Task.CompletedTask;
         }
 
+        public async Task SendChatMessage(IUser sender, string message)
+        {
+            var senderName = await sender.GetName();
+
+            // TODO command parser
+            // construct name
+            Chat jsonData = await CreateStandardChatMessage(senderName, message);
+            byte position = 0; // It represents user message in chat box
+            foreach (var item in _users.Keys)
+            {
+                await item.SendChatMessage(jsonData, position);
+            }
+        }
+
+        public async Task SendChatMessage(IUser sender, IUser receiver, string message)
+        {
+            var senderName = await sender.GetName();
+            var receiverName = await receiver.GetName();
+
+            Chat jsonData = await CreateStandardChatMessage(senderName, message);
+            byte position = 0; // It represents user message in chat box
+            foreach (var item in _users.Keys)
+            {
+                if (await item.GetName() == receiverName ||
+                    await item.GetName() == senderName)
+                    await item.SendChatMessage(jsonData, position);
+            }
+        }
+
         private async Task OnGameTick(object state)
         {
             var now = DateTime.UtcNow;
@@ -62,6 +93,25 @@ namespace MineCase.Server.Game
             await Task.WhenAll(_users.Keys.Select(o => o.OnGameTick(deltaTime)));
         }
 
+        private Task<Chat> CreateStandardChatMessage(string name, string message)
+        {
+            StringComponent nameComponent = new StringComponent(name);
+            nameComponent.ClickEvent = new ChatClickEvent(ClickEventType.SuggestCommand, "/msg " + name);
+            nameComponent.HoverEvent = new ChatHoverEvent(HoverEventType.ShowEntity, name);
+            nameComponent.Insertion = name;
+
+            // construct message
+            StringComponent messageComponent = new StringComponent(message);
+
+            // list
+            List<ChatComponent> list = new List<ChatComponent>();
+            list.Add(nameComponent);
+            list.Add(messageComponent);
+
+            Chat jsonData = new Chat(new TranslationComponent("chat.type.text", list));
+            return Task.FromResult(jsonData);
+        }
+
         private class UserContext
         {
             public ClientPlayPacketGenerator Generator { get; set; }

+ 12 - 0
src/MineCase.Server.Grains/Network/PacketRouterGrain.Play.cs

@@ -4,6 +4,7 @@ using System.IO;
 using System.Numerics;
 using System.Text;
 using System.Threading.Tasks;
+using MineCase.Formats;
 using MineCase.Protocol;
 using MineCase.Protocol.Play;
 using MineCase.Server.Game;
@@ -25,6 +26,11 @@ namespace MineCase.Server.Network
                         innerPacket = TeleportConfirm.Deserialize(br);
                         break;
 
+                    // Chat Message
+                    case 0x03:
+                        innerPacket = ServerboundChatMessage.Deserialize(br);
+                        break;
+
                     // Client Settings
                     case 0x05:
                         innerPacket = ClientSettings.Deserialize(br);
@@ -60,6 +66,12 @@ namespace MineCase.Server.Network
             player.OnTeleportConfirm(packet.TeleportId).Ignore();
         }
 
+        private async Task DispatchPacket(ServerboundChatMessage packet)
+        {
+            var gameSession = await _user.GetGameSession();
+            await gameSession.SendChatMessage(_user, packet.Message);
+        }
+
         private Task DispatchPacket(ClientSettings packet)
         {
             return Task.CompletedTask;

+ 10 - 0
src/MineCase.Server.Grains/Network/Play/ClientPlayPacketGenerator.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using MineCase.Formats;
 using MineCase.Protocol.Play;
 using MineCase.Server.Game;
 using MineCase.Server.Game.Entities;
@@ -112,6 +113,15 @@ namespace MineCase.Server.Network.Play
             });
         }
 
+        public Task SendChatMessage(Chat jsonData, Byte position)
+        {
+            return Sink.SendPacket(new ClientboundChatMessage
+            {
+                JSONData = jsonData,
+                Position = position
+            });
+        }
+
         private Protocol.Play.Slot TransformSlotData(Game.Slot o)
         {
             return new Protocol.Play.Slot

+ 12 - 0
src/MineCase.Server.Grains/User/UserGrain.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Numerics;
 using System.Text;
 using System.Threading.Tasks;
+using MineCase.Formats;
 using MineCase.Server.Game;
 using MineCase.Server.Game.Entities;
 using MineCase.Server.Network;
@@ -142,6 +143,17 @@ namespace MineCase.Server.User
             _state = UserState.DownloadingWorld;
         }
 
+        public Task SendChatMessage(Chat jsonData, byte position)
+        {
+            _generator.SendChatMessage(jsonData, position);
+            return Task.CompletedTask;
+        }
+
+        public Task<String> GetName()
+        {
+            return Task.FromResult(_name);
+        }
+
         public Task SetName(string name)
         {
             _name = name;

+ 6 - 0
src/MineCase.Server.Interfaces/Game/IGameSession.cs

@@ -2,6 +2,8 @@
 using System.Collections.Generic;
 using System.Text;
 using System.Threading.Tasks;
+using MineCase.Formats;
+using MineCase.Protocol.Play;
 using MineCase.Server.User;
 using Orleans;
 
@@ -12,5 +14,9 @@ namespace MineCase.Server.Game
         Task JoinGame(IUser player);
 
         Task LeaveGame(IUser player);
+
+        Task SendChatMessage(IUser sender, String message);
+
+        Task SendChatMessage(IUser sender, IUser receiver, String messages);
     }
 }

+ 5 - 0
src/MineCase.Server.Interfaces/User/IUser.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Numerics;
 using System.Text;
 using System.Threading.Tasks;
+using MineCase.Formats;
 using MineCase.Server.Game;
 using MineCase.Server.Game.Entities;
 using MineCase.Server.Network;
@@ -13,6 +14,8 @@ namespace MineCase.Server.User
 {
     public interface IUser : IGrainWithGuidKey
     {
+        Task<String> GetName();
+
         Task SetName(string name);
 
         Task<uint> GetProtocolVersion();
@@ -35,6 +38,8 @@ namespace MineCase.Server.User
 
         Task KeepAlive(uint keepAliveId);
 
+        Task SendChatMessage(Chat jsonData, Byte position);
+
         Task<uint> GetPing();
 
         Task OnGameTick(TimeSpan deltaTime);