diff --git a/Client/Assets/Scenes/Test.unity b/Client/Assets/Scenes/Test.unity index 61a32da..9ea8d64 100644 --- a/Client/Assets/Scenes/Test.unity +++ b/Client/Assets/Scenes/Test.unity @@ -584,7 +584,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!4 &1388451206 Transform: m_ObjectHideFlags: 0 @@ -639,7 +639,7 @@ Transform: m_GameObject: {fileID: 2036983430} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0.17242, y: 0.05575, z: 0} + m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] @@ -677,7 +677,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: isGlobal: 1 ---- !u!1 &2104915506 +--- !u!1 &2053271181 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -685,37 +685,37 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 2104915508} - - component: {fileID: 2104915507} + - component: {fileID: 2053271183} + - component: {fileID: 2053271182} m_Layer: 0 - m_Name: GrpcClientTest + m_Name: TcpClientTest m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!114 &2104915507 +--- !u!114 &2053271182 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2104915506} + m_GameObject: {fileID: 2053271181} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 98c1b145899d4d97bd981b177b5c45a9, type: 3} + m_Script: {fileID: 11500000, guid: e722dcca5e226864b964cd80358a17f9, type: 3} m_Name: - m_EditorClassIdentifier: ---- !u!4 &2104915508 + m_EditorClassIdentifier: Assembly-CSharp::TcpClientTest +--- !u!4 &2053271183 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2104915506} + m_GameObject: {fileID: 2053271181} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: -0} + m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] @@ -727,6 +727,6 @@ SceneRoots: m_Roots: - {fileID: 1057087090} - {fileID: 613797070} - - {fileID: 2104915508} + - {fileID: 2053271183} - {fileID: 1388451206} - {fileID: 2036983432} diff --git a/Client/Assets/Scripts/Network/GrpcClient.cs b/Client/Assets/Scripts/Network/GrpcClient.cs deleted file mode 100644 index b7f86c0..0000000 --- a/Client/Assets/Scripts/Network/GrpcClient.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Grpc.Net.Client; -using Grpc.Net.Client.Web; -using Protocol; -using System.Net.Http; -using System.Threading.Tasks; -using UnityEngine; -using Utils; - -namespace Network -{ - public class GrpcClient : Singleton - { - // The address must adopt HTTP. - private const string ServerAddress = "http://127.0.0.1:12345"; - - private readonly GrpcChannel _channel; - private readonly GameService.GameServiceClient _game; - - public GrpcClient() - { - var channelOptions = new GrpcChannelOptions - { - HttpHandler = new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler()) - }; - - _channel = GrpcChannel.ForAddress(ServerAddress, channelOptions); - - _game = new GameService.GameServiceClient(_channel); - - Application.quitting += () => _channel.ShutdownAsync().Wait(); - } - - public async Task Login(string username, string password) - { - return await _game.LoginAsync(new LoginRequest { Username = username, Password = password }); - } - } -} \ No newline at end of file diff --git a/Client/Assets/Scripts/Network/GrpcClient.cs.meta b/Client/Assets/Scripts/Network/GrpcClient.cs.meta deleted file mode 100644 index eab447d..0000000 --- a/Client/Assets/Scripts/Network/GrpcClient.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: bd54dd152e0e4bbda802e9aa04078197 -timeCreated: 1752480707 \ No newline at end of file diff --git a/Client/Assets/Scripts/Network/UnityTcpClient.cs b/Client/Assets/Scripts/Network/UnityTcpClient.cs new file mode 100644 index 0000000..15d52a4 --- /dev/null +++ b/Client/Assets/Scripts/Network/UnityTcpClient.cs @@ -0,0 +1,70 @@ +using System; +using System.Net.Sockets; +using System.Threading.Tasks; +using UnityEngine; +using Utils; + +namespace Network +{ + public class UnityTcpClient : Singleton, IDisposable + { + private TcpClient _client; + private bool _disposed; + + public UnityTcpClient() + { + try + { + _client = new TcpClient(); + _client.Connect("127.0.0.1", 12345); + Application.quitting += Dispose; + } + catch (Exception ex) + { + Debug.LogException(ex); + return; + } + } + + public async Task SendAndReceiveData(byte[] data) + { + try + { + await using var stream = _client.GetStream(); + + await stream.WriteAsync(data, 0, data.Length); + + var buffer = new byte[1024]; + await stream.ReadAsync(buffer); + return buffer; + } + catch (Exception ex) + { + Debug.LogException(ex); + return new byte[0]; + } + } + + public void Dispose() + { + if (_disposed) return; + + try + { + _client.Close(); + _client.Dispose(); + } + catch (Exception ex) + { + Debug.LogException(ex); + return; + } + finally + { + _client = null; + } + + _disposed = true; + } + } +} \ No newline at end of file diff --git a/Client/Assets/Scripts/Network/UnityTcpClient.cs.meta b/Client/Assets/Scripts/Network/UnityTcpClient.cs.meta new file mode 100644 index 0000000..17e4749 --- /dev/null +++ b/Client/Assets/Scripts/Network/UnityTcpClient.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 39f71cfa622a1194b812b7b3cc5b86c1 \ No newline at end of file diff --git a/Client/Assets/Scripts/Network/UnityUdpClient.cs b/Client/Assets/Scripts/Network/UnityUdpClient.cs index b682523..f94827c 100644 --- a/Client/Assets/Scripts/Network/UnityUdpClient.cs +++ b/Client/Assets/Scripts/Network/UnityUdpClient.cs @@ -1,3 +1,4 @@ +using System; using System.Net.Sockets; using System.Threading.Tasks; using UnityEngine; @@ -5,24 +6,62 @@ using Utils; namespace Network { - public class UnityUdpClient : Singleton + public class UnityUdpClient : Singleton, IDisposable { - private readonly UdpClient _client; + private UdpClient _client; + private bool _disposed; public UnityUdpClient() { - _client = new UdpClient(); - _client.Connect("127.0.0.1", 12345); - - Application.quitting += () => _client.Close(); + try + { + _client = new UdpClient(); + _client.Connect("127.0.0.1", 12345); + Application.quitting += Dispose; + } + catch (Exception ex) + { + Debug.LogException(ex); + return; + } } public async Task SendAndReceiveData(byte[] data) { - await _client.SendAsync(data, data.Length); + try + { + await _client.SendAsync(data, data.Length); - var result = await _client.ReceiveAsync(); - return result.Buffer; + var result = await _client.ReceiveAsync(); + return result.Buffer; + } + catch (Exception ex) + { + Debug.LogException(ex); + return new byte[0]; + } + } + + public void Dispose() + { + if (_disposed) return; + + try + { + _client.Close(); + _client.Dispose(); + } + catch (Exception ex) + { + Debug.LogException(ex); + return; + } + finally + { + _client = null; + } + + _disposed = true; } } } diff --git a/Client/Assets/Scripts/Protocol/Message.cs b/Client/Assets/Scripts/Protocol/Message.cs index fe2734d..dd43c40 100644 --- a/Client/Assets/Scripts/Protocol/Message.cs +++ b/Client/Assets/Scripts/Protocol/Message.cs @@ -31,10 +31,7 @@ namespace Protocol { "ZRgBIAEoCRIQCghwYXNzd29yZBgCIAEoCSJKCg5TaWdudXBSZXNwb25zZRIn", "CgZyZXN1bHQYASABKA4yFy5wcm90b2NvbC5SZXF1ZXN0UmVzdWx0Eg8KB21l", "c3NhZ2UYAiABKAkqJgoNUmVxdWVzdFJlc3VsdBILCgdTdWNjZXNzEAASCAoE", - "RmFpbBABMoQBCgtHYW1lU2VydmljZRI4CgVMb2dpbhIWLnByb3RvY29sLkxv", - "Z2luUmVxdWVzdBoXLnByb3RvY29sLkxvZ2luUmVzcG9uc2USOwoGU2lnbnVw", - "EhcucHJvdG9jb2wuU2lnbnVwUmVxdWVzdBoYLnByb3RvY29sLlNpZ251cFJl", - "c3BvbnNlYgZwcm90bzM=")); + "RmFpbBABYgZwcm90bzM=")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Protocol.RequestResult), }, null, new pbr::GeneratedClrTypeInfo[] { diff --git a/Client/Assets/Scripts/Protocol/MessageGrpc.cs b/Client/Assets/Scripts/Protocol/MessageGrpc.cs deleted file mode 100644 index 7f7427e..0000000 --- a/Client/Assets/Scripts/Protocol/MessageGrpc.cs +++ /dev/null @@ -1,195 +0,0 @@ -// -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: message.proto -// -#pragma warning disable 0414, 1591, 8981, 0612 -#region Designer generated code - -using grpc = global::Grpc.Core; - -namespace Protocol { - public static partial class GameService - { - static readonly string __ServiceName = "protocol.GameService"; - - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - static void __Helper_SerializeMessage(global::Google.Protobuf.IMessage message, grpc::SerializationContext context) - { - #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION - if (message is global::Google.Protobuf.IBufferMessage) - { - context.SetPayloadLength(message.CalculateSize()); - global::Google.Protobuf.MessageExtensions.WriteTo(message, context.GetBufferWriter()); - context.Complete(); - return; - } - #endif - context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message)); - } - - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - static class __Helper_MessageCache - { - public static readonly bool IsBufferMessage = global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T)); - } - - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - static T __Helper_DeserializeMessage(grpc::DeserializationContext context, global::Google.Protobuf.MessageParser parser) where T : global::Google.Protobuf.IMessage - { - #if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION - if (__Helper_MessageCache.IsBufferMessage) - { - return parser.ParseFrom(context.PayloadAsReadOnlySequence()); - } - #endif - return parser.ParseFrom(context.PayloadAsNewBuffer()); - } - - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - static readonly grpc::Marshaller __Marshaller_protocol_LoginRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Protocol.LoginRequest.Parser)); - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - static readonly grpc::Marshaller __Marshaller_protocol_LoginResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Protocol.LoginResponse.Parser)); - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - static readonly grpc::Marshaller __Marshaller_protocol_SignupRequest = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Protocol.SignupRequest.Parser)); - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - static readonly grpc::Marshaller __Marshaller_protocol_SignupResponse = grpc::Marshallers.Create(__Helper_SerializeMessage, context => __Helper_DeserializeMessage(context, global::Protocol.SignupResponse.Parser)); - - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - static readonly grpc::Method __Method_Login = new grpc::Method( - grpc::MethodType.Unary, - __ServiceName, - "Login", - __Marshaller_protocol_LoginRequest, - __Marshaller_protocol_LoginResponse); - - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - static readonly grpc::Method __Method_Signup = new grpc::Method( - grpc::MethodType.Unary, - __ServiceName, - "Signup", - __Marshaller_protocol_SignupRequest, - __Marshaller_protocol_SignupResponse); - - /// Service descriptor - public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor - { - get { return global::Protocol.MessageReflection.Descriptor.Services[0]; } - } - - /// Base class for server-side implementations of GameService - [grpc::BindServiceMethod(typeof(GameService), "BindService")] - public abstract partial class GameServiceBase - { - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - public virtual global::System.Threading.Tasks.Task Login(global::Protocol.LoginRequest request, grpc::ServerCallContext context) - { - throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, "")); - } - - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - public virtual global::System.Threading.Tasks.Task Signup(global::Protocol.SignupRequest request, grpc::ServerCallContext context) - { - throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, "")); - } - - } - - /// Client for GameService - public partial class GameServiceClient : grpc::ClientBase - { - /// Creates a new client for GameService - /// The channel to use to make remote calls. - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - public GameServiceClient(grpc::ChannelBase channel) : base(channel) - { - } - /// Creates a new client for GameService that uses a custom CallInvoker. - /// The callInvoker to use to make remote calls. - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - public GameServiceClient(grpc::CallInvoker callInvoker) : base(callInvoker) - { - } - /// Protected parameterless constructor to allow creation of test doubles. - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - protected GameServiceClient() : base() - { - } - /// Protected constructor to allow creation of configured clients. - /// The client configuration. - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - protected GameServiceClient(ClientBaseConfiguration configuration) : base(configuration) - { - } - - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - public virtual global::Protocol.LoginResponse Login(global::Protocol.LoginRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) - { - return Login(request, new grpc::CallOptions(headers, deadline, cancellationToken)); - } - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - public virtual global::Protocol.LoginResponse Login(global::Protocol.LoginRequest request, grpc::CallOptions options) - { - return CallInvoker.BlockingUnaryCall(__Method_Login, null, options, request); - } - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - public virtual grpc::AsyncUnaryCall LoginAsync(global::Protocol.LoginRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) - { - return LoginAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); - } - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - public virtual grpc::AsyncUnaryCall LoginAsync(global::Protocol.LoginRequest request, grpc::CallOptions options) - { - return CallInvoker.AsyncUnaryCall(__Method_Login, null, options, request); - } - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - public virtual global::Protocol.SignupResponse Signup(global::Protocol.SignupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) - { - return Signup(request, new grpc::CallOptions(headers, deadline, cancellationToken)); - } - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - public virtual global::Protocol.SignupResponse Signup(global::Protocol.SignupRequest request, grpc::CallOptions options) - { - return CallInvoker.BlockingUnaryCall(__Method_Signup, null, options, request); - } - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - public virtual grpc::AsyncUnaryCall SignupAsync(global::Protocol.SignupRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) - { - return SignupAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); - } - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - public virtual grpc::AsyncUnaryCall SignupAsync(global::Protocol.SignupRequest request, grpc::CallOptions options) - { - return CallInvoker.AsyncUnaryCall(__Method_Signup, null, options, request); - } - /// Creates a new instance of client from given ClientBaseConfiguration. - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - protected override GameServiceClient NewInstance(ClientBaseConfiguration configuration) - { - return new GameServiceClient(configuration); - } - } - - /// Creates service definition that can be registered with a server - /// An object implementing the server-side handling logic. - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - public static grpc::ServerServiceDefinition BindService(GameServiceBase serviceImpl) - { - return grpc::ServerServiceDefinition.CreateBuilder() - .AddMethod(__Method_Login, serviceImpl.Login) - .AddMethod(__Method_Signup, serviceImpl.Signup).Build(); - } - - /// Register service method with a service binder with or without implementation. Useful when customizing the service binding logic. - /// Note: this method is part of an experimental API that can change or be removed without any prior notice. - /// Service methods will be bound by calling AddMethod on this object. - /// An object implementing the server-side handling logic. - [global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)] - public static void BindService(grpc::ServiceBinderBase serviceBinder, GameServiceBase serviceImpl) - { - serviceBinder.AddMethod(__Method_Login, serviceImpl == null ? null : new grpc::UnaryServerMethod(serviceImpl.Login)); - serviceBinder.AddMethod(__Method_Signup, serviceImpl == null ? null : new grpc::UnaryServerMethod(serviceImpl.Signup)); - } - - } -} -#endregion diff --git a/Client/Assets/Scripts/Protocol/MessageGrpc.cs.meta b/Client/Assets/Scripts/Protocol/MessageGrpc.cs.meta deleted file mode 100644 index cdba154..0000000 --- a/Client/Assets/Scripts/Protocol/MessageGrpc.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: f2f787a1d94a6ae48b5586f040f0cf1b \ No newline at end of file diff --git a/Client/Assets/Scripts/Test/GrpcClientTest.cs b/Client/Assets/Scripts/Test/GrpcClientTest.cs deleted file mode 100644 index fd95501..0000000 --- a/Client/Assets/Scripts/Test/GrpcClientTest.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Network; -using UnityEngine; - -namespace Test -{ - public class GrpcClientTest : MonoBehaviour - { - private async void Start() - { - var loginResult = await GrpcClient.Instance.Login("野兽先辈", "114514"); - Debug.Log($"Received login result: {loginResult.Result}"); - } - } -} \ No newline at end of file diff --git a/Client/Assets/Scripts/Test/GrpcClientTest.cs.meta b/Client/Assets/Scripts/Test/GrpcClientTest.cs.meta deleted file mode 100644 index 934631d..0000000 --- a/Client/Assets/Scripts/Test/GrpcClientTest.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 98c1b145899d4d97bd981b177b5c45a9 -timeCreated: 1752478138 \ No newline at end of file diff --git a/Client/Assets/Scripts/Test/TcpClientTest.cs b/Client/Assets/Scripts/Test/TcpClientTest.cs new file mode 100644 index 0000000..dbf4616 --- /dev/null +++ b/Client/Assets/Scripts/Test/TcpClientTest.cs @@ -0,0 +1,19 @@ +using Network; +using System.Text; +using UnityEngine; + +namespace Test +{ + public class TcpClientTest : MonoBehaviour + { + private async void Start() + { + var sendBytes = Encoding.UTF8.GetBytes("This is a test string sent via TCP."); + + var receivedBytes = await UnityTcpClient.Instance.SendAndReceiveData(sendBytes); + var receivedString = Encoding.UTF8.GetString(receivedBytes); + + Debug.Log($"Received string: {receivedString}"); + } + } +} \ No newline at end of file diff --git a/Client/Assets/Scripts/Test/TcpClientTest.cs.meta b/Client/Assets/Scripts/Test/TcpClientTest.cs.meta new file mode 100644 index 0000000..af74bf3 --- /dev/null +++ b/Client/Assets/Scripts/Test/TcpClientTest.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e722dcca5e226864b964cd80358a17f9 \ No newline at end of file diff --git a/Client/Assets/Scripts/Test/UdpClientTest.cs b/Client/Assets/Scripts/Test/UdpClientTest.cs index 58e8a13..bfd2d68 100644 --- a/Client/Assets/Scripts/Test/UdpClientTest.cs +++ b/Client/Assets/Scripts/Test/UdpClientTest.cs @@ -8,7 +8,7 @@ namespace Test { private async void Start() { - var sendBytes = Encoding.UTF8.GetBytes("Test 汉语 and English simultaneously!"); + var sendBytes = Encoding.UTF8.GetBytes("This is a test string sent via UDP."); var receivedBytes = await UnityUdpClient.Instance.SendAndReceiveData(sendBytes); var receivedString = Encoding.UTF8.GetString(receivedBytes); diff --git a/Server/Cargo.lock b/Server/Cargo.lock index 50d6949..6fcee78 100644 --- a/Server/Cargo.lock +++ b/Server/Cargo.lock @@ -710,8 +710,6 @@ dependencies = [ "prettyplease", "prost", "prost-types", - "pulldown-cmark", - "pulldown-cmark-to-cmark", "regex", "syn", "tempfile", @@ -739,26 +737,6 @@ dependencies = [ "prost", ] -[[package]] -name = "pulldown-cmark" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" -dependencies = [ - "bitflags", - "memchr", - "unicase", -] - -[[package]] -name = "pulldown-cmark-to-cmark" -version = "21.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b6a0769a491a08b31ea5c62494a8f144ee0987d86d670a8af4df1e1b7cde75" -dependencies = [ - "pulldown-cmark", -] - [[package]] name = "quote" version = "1.0.40" @@ -871,11 +849,9 @@ dependencies = [ "colored", "log", "prost", + "prost-build", "tokio", "tonic", - "tonic-prost", - "tonic-prost-build", - "tonic-web", ] [[package]] @@ -1029,63 +1005,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "tonic-build" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49e323d8bba3be30833707e36d046deabf10a35ae8ad3cae576943ea8933e25d" -dependencies = [ - "prettyplease", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tonic-prost" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9c511b9a96d40cb12b7d5d00464446acf3b9105fd3ce25437cfe41c92b1c87d" -dependencies = [ - "bytes", - "prost", - "tonic", -] - -[[package]] -name = "tonic-prost-build" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef298fcd01b15e135440c4b8c974460ceca4e6a5af7f1c933b08e4d2875efa1" -dependencies = [ - "prettyplease", - "proc-macro2", - "prost-build", - "prost-types", - "quote", - "syn", - "tempfile", - "tonic-build", -] - -[[package]] -name = "tonic-web" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd70b30990a5e47d404c5b2223c9cc194603ab400d2ee4248099533181e7b747" -dependencies = [ - "base64", - "bytes", - "http", - "http-body", - "pin-project", - "tokio-stream", - "tonic", - "tower-layer", - "tower-service", - "tracing", -] - [[package]] name = "tower" version = "0.5.2" @@ -1154,12 +1073,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "unicase" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" - [[package]] name = "unicode-ident" version = "1.0.18" diff --git a/Server/Cargo.toml b/Server/Cargo.toml index 614b5b3..1d4e36c 100644 --- a/Server/Cargo.toml +++ b/Server/Cargo.toml @@ -12,8 +12,6 @@ log = "0.4" prost = "0.14" tokio = { version = "1", features = ["full"] } tonic = "0.14" -tonic-prost = "0.14" -tonic-web = "0.14" [build-dependencies] -tonic-prost-build = "0.14" +prost-build = "0.14" diff --git a/Server/build.rs b/Server/build.rs index 43ea84c..bcfe5e4 100644 --- a/Server/build.rs +++ b/Server/build.rs @@ -5,6 +5,11 @@ fn main() -> io::Result<()> { unsafe { env::set_var("PROTOC", "../Tools/ProtoBuf/bin/protoc.exe"); } - tonic_prost_build::compile_protos("../Tools/ProtoBuf/proto/message.proto")?; + + prost_build::compile_protos( + &["../Tools/ProtoBuf/proto/message.proto"], + &["../Tools/ProtoBuf/proto"], + )?; + Ok(()) } diff --git a/Server/src/command_helper.rs b/Server/src/command_helper.rs index f074ccb..351a9e9 100644 --- a/Server/src/command_helper.rs +++ b/Server/src/command_helper.rs @@ -1,6 +1,9 @@ use std::io::Write; -pub(crate) fn run() { +use crate::servers::tcp_server::TCP_SERVER; +use crate::servers::udp_server::UDP_SERVER; + +pub(crate) async fn run() { let stdin = std::io::stdin(); loop { @@ -14,7 +17,12 @@ pub(crate) fn run() { .expect("Failed to read from standard input!"); match input.trim() { - "exit" => break, + "exit" => { + TCP_SERVER.lock().await.stop().await; + UDP_SERVER.lock().await.stop().await; + + break; + } _ => println!("Usage: "), } } diff --git a/Server/src/grpc_server.rs b/Server/src/grpc_server.rs deleted file mode 100644 index 8795476..0000000 --- a/Server/src/grpc_server.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::net::SocketAddr; - -use tokio::task; -use tonic::transport::Server; -use tonic_web::GrpcWebLayer; - -use crate::protocol::game_service_server::GameServiceServer; -use crate::services::game_service::GameServiceImpl; - -pub(crate) struct GrpcServer; - -impl GrpcServer { - pub(crate) async fn init() { - let addr = SocketAddr::new([127, 0, 0, 1].into(), 12345); - - let game_service = GameServiceServer::new(GameServiceImpl); - - task::spawn(async move { - Server::builder() - .accept_http1(true) - .layer(GrpcWebLayer::new()) - .add_service(game_service) - .serve(addr) - .await - .unwrap_or_else(|e| log::error!("Failed to build server: {e}")); - }); - } -} diff --git a/Server/src/main.rs b/Server/src/main.rs index e0563a4..6fb867e 100644 --- a/Server/src/main.rs +++ b/Server/src/main.rs @@ -1,13 +1,12 @@ mod command_helper; -mod grpc_server; mod protocol; mod server_logger; +mod servers; mod services; -mod udp_server; -use grpc_server::GrpcServer; use server_logger::ServerLogger; -use udp_server::UdpServer; +use servers::tcp_server::TCP_SERVER; +use servers::udp_server::UDP_SERVER; #[tokio::main] async fn main() { @@ -15,10 +14,10 @@ async fn main() { log::info!("Starting server..."); - GrpcServer::init().await; - UdpServer::init(); + TCP_SERVER.lock().await.start().await; + UDP_SERVER.lock().await.start().await; log::info!("Server successfully started!"); - command_helper::run(); + command_helper::run().await; } diff --git a/Server/src/servers.rs b/Server/src/servers.rs new file mode 100644 index 0000000..e55b41d --- /dev/null +++ b/Server/src/servers.rs @@ -0,0 +1,4 @@ +pub(crate) mod tcp_server; +pub(crate) mod udp_server; + +const SERVER_ADDR: &str = "127.0.0.1:12345"; diff --git a/Server/src/servers/tcp_server.rs b/Server/src/servers/tcp_server.rs new file mode 100644 index 0000000..74c5aaf --- /dev/null +++ b/Server/src/servers/tcp_server.rs @@ -0,0 +1,120 @@ +use std::collections::HashMap; +use std::io; +use std::net::SocketAddr; +use std::sync::LazyLock; + +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::{TcpListener, TcpStream}; +use tokio::sync::{Mutex, mpsc}; +use tokio::task::JoinHandle; + +use super::SERVER_ADDR; + +pub(crate) static TCP_SERVER: LazyLock> = + LazyLock::new(|| Mutex::new(TcpServer::new())); + +pub(crate) struct TcpServer { + is_running: bool, + clients: HashMap>, + shutdown_tx: Option>, +} + +impl TcpServer { + fn new() -> Self { + Self { + is_running: false, + clients: HashMap::new(), + shutdown_tx: None, + } + } + + pub(crate) async fn start(&mut self) { + if self.is_running { + log::warn!("TCP server is already running"); + return; + } + + match TcpListener::bind(SERVER_ADDR).await { + Ok(listener) => { + let (shutdown_tx, shutdown_rx) = mpsc::channel(1); + self.is_running = true; + self.shutdown_tx = Some(shutdown_tx); + + tokio::spawn(async move { + Self::listen_to_clients(listener, shutdown_rx).await; + }); + + log::info!("TCP Server started on {}", SERVER_ADDR); + } + Err(e) => log::error!("Failed to bind to address: {e}"), + } + } + + pub(crate) async fn stop(&mut self) { + if !self.is_running { + return; + } + + self.is_running = false; + + if let Some(shutdown_tx) = self.shutdown_tx.take() { + _ = shutdown_tx.send(()).await; + } + + for (addr, connection) in self.clients.drain() { + log::info!("Closing connection to {}", addr); + connection.abort(); + } + } + + async fn listen_to_clients(listener: TcpListener, mut shutdown_rx: mpsc::Receiver<()>) { + loop { + tokio::select! { + result = listener.accept() => { + match result { + Ok((socket, addr)) => { + log::info!("New client connected: {addr}"); + + let task_handle = tokio::spawn(async move { + if let Err(e) = Self::handle_client(socket, addr).await { + log::error!("Client {addr} error: {e}"); + } + log::info!("Client {addr} disconnected"); + }); + + let mut server = TCP_SERVER.lock().await; + server.clients.insert(addr, task_handle); + } + Err(e) => log::error!("Couldn't get client: {e}"), + } + } + + _ = shutdown_rx.recv() => { + log::info!("TCP Server shutting down"); + break; + } + } + } + } + + async fn handle_client(mut socket: TcpStream, addr: SocketAddr) -> io::Result<()> { + let mut buffer = [0; 1024]; + + loop { + let len = socket.read(&mut buffer).await?; + if len == 0 { + break; + } + + log::debug!("Received {} bytes from {}", len, addr); + + // TODO: Deserialize data + socket.write_all(&buffer[..len]).await?; + } + + let mut server = TCP_SERVER.lock().await; + server.clients.remove(&addr); + + Ok(()) + } +} diff --git a/Server/src/servers/udp_server.rs b/Server/src/servers/udp_server.rs new file mode 100644 index 0000000..86ed477 --- /dev/null +++ b/Server/src/servers/udp_server.rs @@ -0,0 +1,68 @@ +use std::io; +use std::sync::LazyLock; + +use tokio::net::UdpSocket; +use tokio::sync::Mutex; + +use super::SERVER_ADDR; + +pub(crate) static UDP_SERVER: LazyLock> = + LazyLock::new(|| Mutex::new(UdpServer::new())); + +pub(crate) struct UdpServer { + is_running: bool, +} + +impl UdpServer { + fn new() -> Self { + Self { is_running: false } + } + + pub(crate) async fn start(&mut self) { + if self.is_running { + log::warn!("UDP server is already running"); + return; + } + + match UdpSocket::bind(SERVER_ADDR).await { + Ok(socket) => { + self.is_running = true; + + tokio::spawn(async move { + Self::handle_client(&socket) + .await + .unwrap_or_else(|e| log::error!("Failed to process data: {e}")) + }); + + log::info!("UDP Server started on {}", SERVER_ADDR); + } + Err(e) => log::error!("Failed to bind to address: {e}"), + } + } + + pub(crate) async fn stop(&mut self) { + if !self.is_running { + return; + } + + self.is_running = false; + } + + async fn handle_client(socket: &UdpSocket) -> io::Result<()> { + loop { + let mut buffer = [0; 1024]; + let (len, addr) = socket.recv_from(&mut buffer).await?; + if len == 0 { + break; + } + + log::info!("Received message from client {addr}"); + + // TODO: Deserialize data + let buffer = &buffer[..len]; + socket.send_to(buffer, addr).await?; + } + + Ok(()) + } +} diff --git a/Server/src/services/game_service.rs b/Server/src/services/game_service.rs index 00bccc4..8b13789 100644 --- a/Server/src/services/game_service.rs +++ b/Server/src/services/game_service.rs @@ -1,33 +1 @@ -use tonic::{Request, Response, Status}; -use crate::protocol::game_service_server::GameService; -use crate::protocol::{LoginRequest, LoginResponse, RequestResult, SignupRequest, SignupResponse}; - -pub(crate) struct GameServiceImpl; - -#[tonic::async_trait] -impl GameService for GameServiceImpl { - async fn login( - &self, - request: Request, - ) -> Result, Status> { - log::info!("User {} logged in!", request.get_ref().username); - - Ok(Response::new(LoginResponse { - result: RequestResult::Success.into(), - message: "".into(), - })) - } - - async fn signup( - &self, - request: Request, - ) -> Result, Status> { - log::info!("User {} signed up!", request.get_ref().username); - - Ok(Response::new(SignupResponse { - result: RequestResult::Success.into(), - message: "".into(), - })) - } -} diff --git a/Server/src/udp_server.rs b/Server/src/udp_server.rs deleted file mode 100644 index 117de73..0000000 --- a/Server/src/udp_server.rs +++ /dev/null @@ -1,27 +0,0 @@ -use std::net::UdpSocket; - -use tokio::task; - -pub(crate) struct UdpServer; - -impl UdpServer { - pub(crate) fn init() { - match UdpSocket::bind("127.0.0.1:12345") { - Ok(socket) => { - task::spawn(async move { - loop { - let mut buf = [0; 1500]; - let (amt, src) = socket.recv_from(&mut buf).unwrap(); - - log::info!("Received message from client {src}"); - - // TODO: Process received data in an independent method. - let buf = &buf[..amt]; - socket.send_to(buf, src).unwrap(); - } - }); - } - Err(e) => log::error!("Failed to bind to address: {e}"), - } - } -} diff --git a/Tools/ProtoBuf/bin/grpc_csharp_plugin.exe b/Tools/ProtoBuf/bin/grpc_csharp_plugin.exe deleted file mode 100644 index 0f3fd1c..0000000 Binary files a/Tools/ProtoBuf/bin/grpc_csharp_plugin.exe and /dev/null differ diff --git a/Tools/ProtoBuf/gen.py b/Tools/ProtoBuf/gen.py index 837d09f..1673ddb 100644 --- a/Tools/ProtoBuf/gen.py +++ b/Tools/ProtoBuf/gen.py @@ -1,35 +1,30 @@ -from subprocess import Popen -from os import path +import subprocess import os import shutil protoc_dir = "./bin/protoc.exe" -grpc_cs_plugin_dir = "./bin/grpc_csharp_plugin.exe" proto_dir = "./proto" -message_dir = proto_dir + "/message.proto" +message_dir = f"{proto_dir}/message.proto" gen_dir = "./gen" gen_code_dst_dir = "../../Client/Assets/Scripts/Protocol" def GenerateProtocol(): - if not path.exists(gen_dir): + if not os.path.exists(gen_dir): os.makedirs(gen_dir) - result = Popen( + gen_code = subprocess.Popen( [ protoc_dir, f"--proto_path={proto_dir}", f"--csharp_out={gen_dir}", - f"--grpc_out={gen_dir}", - f"--plugin=protoc-gen-grpc={grpc_cs_plugin_dir}", message_dir, ] ) - - result.wait() + gen_code.wait() def MoveGeneratedCode(): diff --git a/Tools/ProtoBuf/proto/message.proto b/Tools/ProtoBuf/proto/message.proto index 556998a..bbad498 100644 --- a/Tools/ProtoBuf/proto/message.proto +++ b/Tools/ProtoBuf/proto/message.proto @@ -2,15 +2,6 @@ syntax = "proto3"; package protocol; -// Define services - -service GameService { - rpc Login(LoginRequest) returns (LoginResponse); - rpc Signup(SignupRequest) returns (SignupResponse); -} - -// Define messages - enum RequestResult { Success = 0; Fail = 1;