From 4f18f41389f361fd8f0f540e2af026697dfafd21 Mon Sep 17 00:00:00 2001 From: Mikhail Mitrofanov Date: Sat, 3 May 2025 21:43:02 +0300 Subject: [PATCH] Client udp service --- Example.Backend/Printer.cs | 5 ++ Example.Frontend/ClientBasedPrinter.cs | 5 ++ Example.Frontend/Program.cs | 2 + Example.Shared/IPrinter.cs | 2 + mROA.Codegen/mROASourceGenerator.cs | 42 ++++++++---- mROA/Abstract/IRepresentationModule.cs | 1 + mROA/Abstract/IUntrustedInteractionModule.cs | 11 ++++ .../Attributes/UntrustedAttribute.cs | 8 +++ mROA/Implementation/RemoteObjectBase.cs | 9 +++ mROA/Implementation/RepresentationModule.cs | 9 ++- .../Implementation/UdpUntrustedInteraction.cs | 64 +++++++++++++++++++ 11 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 mROA/Abstract/IUntrustedInteractionModule.cs create mode 100644 mROA/Implementation/Attributes/UntrustedAttribute.cs create mode 100644 mROA/Implementation/UdpUntrustedInteraction.cs diff --git a/Example.Backend/Printer.cs b/Example.Backend/Printer.cs index 87958c9..dba9a98 100644 --- a/Example.Backend/Printer.cs +++ b/Example.Backend/Printer.cs @@ -10,6 +10,11 @@ namespace Example.Backend { public string Name; + public async Task SomeoneIsApproaching(string humanName) + { + Console.WriteLine(humanName + " is approaching"); + } + public void OnPrintExternal(IPage p0, RequestContext ro) { OnPrint?.Invoke(p0, ro); diff --git a/Example.Frontend/ClientBasedPrinter.cs b/Example.Frontend/ClientBasedPrinter.cs index 6afc7a3..1233514 100644 --- a/Example.Frontend/ClientBasedPrinter.cs +++ b/Example.Frontend/ClientBasedPrinter.cs @@ -8,6 +8,11 @@ namespace Example.Frontend { public class ClientBasedPrinter : IPrinter { + public Task SomeoneIsApproaching(string humanName) + { + return Task.CompletedTask; + } + public void OnPrintExternal(IPage p0, RequestContext ro) { } diff --git a/Example.Frontend/Program.cs b/Example.Frontend/Program.cs index cf79fd0..97e7880 100644 --- a/Example.Frontend/Program.cs +++ b/Example.Frontend/Program.cs @@ -1,5 +1,6 @@ using System; using System.Net; +using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -120,6 +121,7 @@ class Program DemoCheck.Show(); Console.ReadKey(); + // // const int iterations = 10000; // var timer = Stopwatch.StartNew(); diff --git a/Example.Shared/IPrinter.cs b/Example.Shared/IPrinter.cs index f5b25f0..505d614 100644 --- a/Example.Shared/IPrinter.cs +++ b/Example.Shared/IPrinter.cs @@ -13,5 +13,7 @@ namespace Example.Shared string GetName(); Task Print(string text, bool someParameter, RequestContext context, CancellationToken cancellationToken); event Action OnPrint; + [Untrusted] + Task SomeoneIsApproaching(string humanName); } } \ No newline at end of file diff --git a/mROA.Codegen/mROASourceGenerator.cs b/mROA.Codegen/mROASourceGenerator.cs index 6329ab2..9f05e44 100644 --- a/mROA.Codegen/mROASourceGenerator.cs +++ b/mROA.Codegen/mROASourceGenerator.cs @@ -291,23 +291,38 @@ namespace mROA.Codegen $"{method.ReturnType.ToUnityString()} {method.Name}({string.Join(", ", method.Parameters.Select(ToFullString))}){{"); + var isUntrusted = method.GetAttributes().Any(i => i.AttributeClass.Name == "UntrustedAttribute"); + var prefix = isAsync ? "await " : ""; var postfix = !isAsync ? isVoid ? ".Wait()" : ".GetAwaiter().GetResult()" : ""; var parameterLink = isParametrized ? ", new System.Object[] { " + string.Join(", ", parameters.Select(i => i.Name)) + " }" : string.Empty; - var tokenInsert = isAsync && method.Parameters.FirstOrDefault(i => i.Type.Name == "CancellationToken") is - { } tokenSymbol - ? ", cancellationToken : " + tokenSymbol.Name - : string.Empty; - var caller = isVoid - ? $"CallAsync({index}{parameterLink}{tokenInsert})" - : isAsync - ? $"GetResultAsync<{ExtractTaskType(method.ReturnType)}>({index}{parameterLink}{tokenInsert})" - : $"GetResultAsync<{ToFullString(method.ReturnType)}>({index}{parameterLink}{tokenInsert})"; - if (!isVoid) - prefix = "return " + prefix; + string caller; + + if (isUntrusted) + { + caller = $"CallUntrustedAsync({index}{parameterLink})"; + } + else + { + var tokenInsert = isAsync && + method.Parameters.FirstOrDefault(i => i.Type.Name == "CancellationToken") is + { } tokenSymbol + ? ", cancellationToken : " + tokenSymbol.Name + : string.Empty; + + caller = isVoid + ? $"CallAsync({index}{parameterLink}{tokenInsert})" + : isAsync + ? $"GetResultAsync<{ExtractTaskType(method.ReturnType)}>({index}{parameterLink}{tokenInsert})" + : $"GetResultAsync<{ToFullString(method.ReturnType)}>({index}{parameterLink}{tokenInsert})"; + + if (!isVoid) + prefix = "return " + prefix; + + } sb.AppendLine("\t\t\t" + prefix + caller + postfix + ";"); @@ -482,8 +497,8 @@ namespace mROA.Codegen var parameterTypes = string.Join(", ", $"{string.Join(", ", method.Parameters.Select(p => "typeof(" + p.Type.ToUnityString() + ")"))}"); var parameterInserts = string.Join(", ", - method.Parameters.Select( - p => Caster(p.Type, "parameters[" + method.Parameters.IndexOf(p) + "]"))); + method.Parameters.Select(p => + Caster(p.Type, "parameters[" + method.Parameters.IndexOf(p) + "]"))); var invokerTemplate = (TemplateDocument)_methodInvokerOriginal.Clone(); invokerTemplate.AddDefine("isVoid", "false"); @@ -612,7 +627,6 @@ namespace mROA.Codegen return parts.ToUnityString(); return type.ToDisplayString(); - } public static string ToUnityString(this IParameterSymbol parameter) diff --git a/mROA/Abstract/IRepresentationModule.cs b/mROA/Abstract/IRepresentationModule.cs index bca9a76..be692ea 100644 --- a/mROA/Abstract/IRepresentationModule.cs +++ b/mROA/Abstract/IRepresentationModule.cs @@ -19,6 +19,7 @@ namespace mROA.Abstract Task PostCallMessageAsync(Guid id, EMessageType eMessageType, T payload) where T : notnull; void PostCallMessage(Guid id, EMessageType eMessageType, T payload) where T : notnull; + Task PostCallMessageUntrustedAsync(Guid id, EMessageType eMessageType, T payload) where T : notnull; void PostCallMessage(Guid id, EMessageType eMessageType, object payload, Type payloadType); } } \ No newline at end of file diff --git a/mROA/Abstract/IUntrustedInteractionModule.cs b/mROA/Abstract/IUntrustedInteractionModule.cs new file mode 100644 index 0000000..151902b --- /dev/null +++ b/mROA/Abstract/IUntrustedInteractionModule.cs @@ -0,0 +1,11 @@ +using System; +using System.Net; +using System.Threading.Tasks; + +namespace mROA.Abstract +{ + public interface IUntrustedInteractionModule : IInjectableModule, IDisposable + { + Task Start(IPEndPoint endpoint); + } +} \ No newline at end of file diff --git a/mROA/Implementation/Attributes/UntrustedAttribute.cs b/mROA/Implementation/Attributes/UntrustedAttribute.cs new file mode 100644 index 0000000..2441590 --- /dev/null +++ b/mROA/Implementation/Attributes/UntrustedAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace mROA.Implementation.Attributes +{ + public class UntrustedAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/mROA/Implementation/RemoteObjectBase.cs b/mROA/Implementation/RemoteObjectBase.cs index c12a07b..264fd4b 100644 --- a/mROA/Implementation/RemoteObjectBase.cs +++ b/mROA/Implementation/RemoteObjectBase.cs @@ -137,6 +137,15 @@ namespace mROA.Implementation } } + protected async Task CallUntrustedAsync(int methodId, object?[]? parameters = null) + { + var request = new DefaultCallRequest + { + CommandId = methodId, ObjectId = _identifier, Parameters = parameters + }; + await _representationModule.PostCallMessageUntrustedAsync(request.Id, EMessageType.CallRequest, request); + } + public override string ToString() { return _identifier.ToString(); diff --git a/mROA/Implementation/RepresentationModule.cs b/mROA/Implementation/RepresentationModule.cs index 761472b..d2f01e9 100644 --- a/mROA/Implementation/RepresentationModule.cs +++ b/mROA/Implementation/RepresentationModule.cs @@ -37,7 +37,7 @@ namespace mROA.Implementation var writer = _interaction.ReceiveChanel.Writer; var reader = _interaction.ReceiveChanel.Reader; - + await foreach (var message in reader.ReadAllAsync(token)) { if (!rule(message)) @@ -95,6 +95,13 @@ namespace mROA.Implementation PostCallMessageAsync(id, eMessageType, payload).GetAwaiter().GetResult(); } + public async Task PostCallMessageUntrustedAsync(Guid id, EMessageType eMessageType, T payload) where T : notnull + { + var serialized = _serialization.Serialize(payload, typeof(T)); + await _interaction.PostMessageUntrustedAsync(new NetworkMessageHeader + { Id = id, MessageType = eMessageType, Data = serialized }); + } + public void PostCallMessage(Guid id, EMessageType eMessageType, object payload, Type payloadType) { PostCallMessageAsync(id, eMessageType, payload, payloadType).GetAwaiter().GetResult(); diff --git a/mROA/Implementation/UdpUntrustedInteraction.cs b/mROA/Implementation/UdpUntrustedInteraction.cs new file mode 100644 index 0000000..84e067d --- /dev/null +++ b/mROA/Implementation/UdpUntrustedInteraction.cs @@ -0,0 +1,64 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; +using mROA.Abstract; + +namespace mROA.Implementation +{ + public class UdpUntrustedInteraction : IUntrustedInteractionModule + { + private ISerializationToolkit _serializationToolkit; + private IChannelInteractionModule _channelInteractionModule; + private CancellationTokenSource _tokenSource = new CancellationTokenSource(); + public void Dispose() + { + _tokenSource.Cancel(); + } + + public Task Start(IPEndPoint endpoint) + { + return Task.Run(() => + { + var client = new UdpClient(); + client.Connect(endpoint); + Listening(client, _tokenSource.Token); + Posting(client, _tokenSource.Token); + }, _tokenSource.Token); + } + + private async Task Listening(UdpClient udpClient, CancellationToken token) + { + var writer = _channelInteractionModule.ReceiveChanel.Writer; + while (token.IsCancellationRequested == false) + { + var message = new Memory((await udpClient.ReceiveAsync()).Buffer); + var parsed = _serializationToolkit.Deserialize(message.Span)!; + await writer.WriteAsync(parsed, token); + } + } + + private async Task Posting(UdpClient udpClient, CancellationToken token) + { + await foreach (var post in _channelInteractionModule.UntrustedPostChanel.ReadAllAsync(token)) + { + var serialized = _serializationToolkit.Serialize(post); + await udpClient.SendAsync(serialized, serialized.Length); + } + } + + public void Inject(T dependency) + { + switch (dependency) + { + case IChannelInteractionModule channelModule: + _channelInteractionModule = channelModule; + break; + case ISerializationToolkit serializationToolkit: + _serializationToolkit = serializationToolkit; + break; + } + } + } +} \ No newline at end of file