diff --git a/Game.cs b/Game.cs index 3b2b8aa..470a2ef 100644 --- a/Game.cs +++ b/Game.cs @@ -112,9 +112,11 @@ namespace Wolf logger.Log($"Spieler {p.Name} wacht auf und sieht sich seine Rolle an."); ttsClient.Read(logger.getLastLine(), readHistory); ShowRole(p); + Task.Delay(5000).Wait(); logger.Log($"Spieler {p.Name} hat seine Rolle gesehen und geht wieder schlafen."); ttsClient.Read(logger.getLastLine(), readHistory); getConfirmationEveryoneAsleep(); + } Console.Clear(); @@ -360,46 +362,73 @@ namespace Wolf { try { - int res = logicClient.GetPlayerId(targets, pois).WaitAsync(TimeSpan.FromSeconds(10)).Result; + int res = logicClient.GetPlayerId(targets, pois).WaitAsync(TimeSpan.FromSeconds(30)).Result; if (res >= 0) { return res; } - } - catch - { + logger.Log("GetPlayerId returned invalid value, defaulting to 0"); + return 0; + } + catch (TimeoutException) + { + logger.Log("GetPlayerId timed out after 30 seconds"); + return 0; + } + catch (Exception ex) + { + logger.Log($"GetPlayerId error: {ex.Message}"); return 0; } - return 0; } public bool getConfirmation(List players) { try { - bool res = logicClient.GetConfirmation(players).WaitAsync(TimeSpan.FromSeconds(10)).Result; - if (res) - { - return true; - } + bool res = logicClient.GetConfirmation(players).WaitAsync(TimeSpan.FromSeconds(30)).Result; + return res; } - catch + catch (TimeoutException) { + logger.Log("GetConfirmation timed out after 30 seconds, defaulting to false"); + return false; + } + catch (Exception ex) + { + logger.Log($"GetConfirmation error: {ex.Message}"); return false; } - return false; } public bool getConfirmationEveryoneAsleep() { - try + int maxRetries = 50; + int attempt = 0; + + while (attempt < maxRetries) { - return logicClient.GetConfirmationEveryoneAsleep().WaitAsync(TimeSpan.FromSeconds(30)).Result; - } - catch - { - return false; + attempt++; + try + { + bool result = logicClient.GetConfirmationEveryoneAsleep().WaitAsync(TimeSpan.FromSeconds(60)).Result; + if (result) + { + return true; + } + logger.Log($"GetConfirmationEveryoneAsleep returned false on attempt {attempt}, retrying..."); + } + catch (TimeoutException) + { + logger.Log($"GetConfirmationEveryoneAsleep timed out on attempt {attempt}/{maxRetries}, retrying..."); + } + catch (Exception ex) + { + logger.Log($"GetConfirmationEveryoneAsleep error on attempt {attempt}/{maxRetries}: {ex.Message}, retrying..."); + } } + + logger.Log($"GetConfirmationEveryoneAsleep failed after {maxRetries} attempts, defaulting to false"); return false; } } diff --git a/OSCClient.cs b/OSCClient.cs index 2fe9c7a..dfcb62e 100644 --- a/OSCClient.cs +++ b/OSCClient.cs @@ -8,65 +8,103 @@ using System.Net; using System.Net.Sockets; using System.Security.Cryptography; using System.Text; +using System.Threading; namespace Wolf { internal class OSCClient { - UdpClient udpClientSend; - UdpClient udpClientReceive; + private UdpClient udpClientSend; + private UdpClient udpClientReceive; + private readonly SemaphoreSlim _requestSemaphore; + private string _lastSentAddress = ""; + private object[] _lastSentArguments = Array.Empty(); public OSCClient(string hostname, int port) { // 1. Lokalen Port binden (für Empfang) udpClientSend = new UdpClient(hostname, port); udpClientReceive = new UdpClient(new IPEndPoint(IPAddress.Any, port)); - - + _requestSemaphore = new SemaphoreSlim(1, 1); } public async Task SendMessage(string endpoint) { - - var message = new OscMessage(new Address(endpoint)); - - await udpClientSend.SendMessageAsync(message); + await _requestSemaphore.WaitAsync(); + try + { + var message = new OscMessage(new Address(endpoint)); + _lastSentAddress = endpoint; + _lastSentArguments = Array.Empty(); + await udpClientSend.SendMessageAsync(message); + } + finally + { + _requestSemaphore.Release(); + } } public async Task SpeakId(int textId) { - var message = new OscMessage( - address: new Address("/speak"), - arguments: new object[] - { - textId - }); - - await udpClientSend.SendMessageAsync(message); + await _requestSemaphore.WaitAsync(); + try + { + var message = new OscMessage( + address: new Address("/speak"), + arguments: new object[] + { + textId + }); + _lastSentAddress = "/speak"; + _lastSentArguments = new object[] { textId }; + await udpClientSend.SendMessageAsync(message); + } + finally + { + _requestSemaphore.Release(); + } } public async Task Read(string text, bool readStory = true) { - string endpoint = readStory ? "/read_story" : "/read"; - var message = new OscMessage( - address: new Address(endpoint), - arguments: new object[] - { - text, 2 - }); - await udpClientSend.SendMessageAsync(message); + await _requestSemaphore.WaitAsync(); + try + { + string endpoint = readStory ? "/read_story" : "/read"; + var message = new OscMessage( + address: new Address(endpoint), + arguments: new object[] + { + text, 2 + }); + _lastSentAddress = endpoint; + _lastSentArguments = new object[] { text, 2 }; + await udpClientSend.SendMessageAsync(message); + } + finally + { + _requestSemaphore.Release(); + } } public async Task SendMessageWithArguments(string endpoint, params object[] param) { - - var message = new OscMessage( - address: new Address(endpoint), - arguments: param - ); - - await udpClientSend.SendMessageAsync(message); + await _requestSemaphore.WaitAsync(); + try + { + var message = new OscMessage( + address: new Address(endpoint), + arguments: param + ); + _lastSentAddress = endpoint; + _lastSentArguments = param; + await udpClientSend.SendMessageAsync(message); + } + finally + { + _requestSemaphore.Release(); + } } @@ -100,20 +138,101 @@ namespace Wolf } } + /// + /// Generische Methode zur Ausführung von Request-Response-Kommunikation mit Retry-Logik. + /// + private async Task ExecuteRequestWithRetry( + string expectedAddress, + OscMessage requestMessage, + Func responseParser, + int maxRetries = 50, + int timeoutSeconds = 20) + { + await _requestSemaphore.WaitAsync(); + try + { + _lastSentAddress = requestMessage.Address.Value; + _lastSentArguments = requestMessage.Arguments.ToArray(); + + for (int attempt = 1; attempt <= maxRetries; attempt++) + { + try + { + // Anfrage senden + await udpClientSend.SendMessageAsync(requestMessage); + + // Auf Response warten mit Timeout + var receiveTask = udpClientReceive.ReceiveMessageAsync(); + var timeoutTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds)); + + var completed = await Task.WhenAny(receiveTask, timeoutTask); + + if (completed == receiveTask) + { + var response = await receiveTask; + + // Prüfen, ob es die erwartete Antwort ist + if (response.Address.Value == expectedAddress) + { + try + { + return responseParser(response); + } + catch (Exception parseEx) + { + // Argument-Parsing-Fehler, ignorieren und nächste Anfrage senden + System.Diagnostics.Debug.WriteLine($"Parse error on attempt {attempt}: {parseEx.Message}"); + continue; + } + } + // Falsche Antwort → ignorieren und weiter versuchen + } + // Timeout → erneut versuchen + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Request attempt {attempt} failed: {ex.Message}"); + } + } + + // Alle Versuche fehlgeschlagen + throw new TimeoutException($"Request to {expectedAddress} failed after {maxRetries} attempts"); + } + finally + { + _requestSemaphore.Release(); + } + } + public async Task> GetAllIds() { - SendMessage("/getAllIds"); - var response = await udpClientReceive.ReceiveMessageAsync(); - if (response.Address.Value == "/getAllIds") + var message = new OscMessage(new Address("/getAllIds")); + + try { - List playerIds = new List(); - foreach (var argument in response.Arguments) - { - playerIds.Add((int)argument); - } - return playerIds; + var result = await ExecuteRequestWithRetry( + expectedAddress: "/getAllIds", + requestMessage: message, + responseParser: response => + { + var playerIds = new List(); + foreach (var argument in response.Arguments) + { + playerIds.Add((int)argument); + } + return playerIds; + }); + return result; + } + catch (TimeoutException) + { + return new List(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"GetAllIds error: {ex.Message}"); + return new List(); } - return new List(); } // < 0 = no confirmation; 0 = confirm; > 0 = player id @@ -139,7 +258,6 @@ namespace Wolf public async Task GetPlayerId(List targets, List pois) { - //return (int) targets.FirstOrDefault().id; // Arrays extrahieren var arr2 = targets.Select(p => p.id).ToArray(); var arr1 = pois.Select(p => p.id).ToArray(); @@ -156,121 +274,117 @@ namespace Wolf foreach (var id in arr2) args.Add(id); - // Nachricht senden + // Nachricht vorbereiten var message = new OscMessage( new Address("/getPlayerID"), args.ToArray() ); - int maxRetries = 50; - int timeoutSeconds = 20; - // Anfrage senden - await udpClientSend.SendMessageAsync(message); - - for (int attempt = 1; attempt <= maxRetries; attempt++) + try { - // Timeout für Receive - var receiveTask = udpClientReceive.ReceiveMessageAsync(); - var timeoutTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds)); - - var completed = await Task.WhenAny(receiveTask, timeoutTask); - - if (completed == receiveTask) - { - var response = await receiveTask; - - // Prüfen, ob es die erwartete Antwort ist - if (response.Address.Value == "/getPlayerID") + var result = await ExecuteRequestWithRetry( + expectedAddress: "/getPlayerID", + requestMessage: message, + responseParser: response => { - return (int)response.Arguments.ElementAt(0); - } - - // Falsche Antwort → ignorieren und weiter versuchen - continue; - } - - await udpClientSend.SendMessageAsync(message); + if (response.Arguments.Count() > 0) + { + return (int)response.Arguments.ElementAt(0); + } + throw new InvalidOperationException("No arguments in response"); + }, + maxRetries: 50, + timeoutSeconds: 20); + return result; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"GetPlayerId error: {ex.Message}"); + return -1; } - - return -1; } public async Task GetConfirmation(List players) { - int maxRetries = 50; - int timeoutSeconds = 20; - var args = new List(); foreach (var p in players) { args.Add(p.id); } - // Anfrage senden + var message = new OscMessage( - new Address("/confirmation"), - args.ToArray() + new Address("/confirmation"), + args.ToArray() ); - await udpClientSend.SendMessageAsync(message); - - //await udpClientReceive.ReceiveMessageAsync(); - - for (int attempt = 1; attempt <= maxRetries; attempt++) + try { - // Timeout für Receive - var receiveTask = udpClientReceive.ReceiveMessageAsync(); - var timeoutTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds)); - - var completed = await Task.WhenAny(receiveTask, timeoutTask); - - if (completed == receiveTask) - { - var response = await receiveTask; - - // Prüfen, ob es die erwartete Antwort ist - if (response.Address.Value == "/confirmation") + var result = await ExecuteRequestWithRetry( + expectedAddress: "/confirmation", + requestMessage: message, + responseParser: response => { - if (response.Arguments.ElementAt(0) is OscFalse) + if (response.Arguments.Count() > 0) { - return false; + var firstArg = response.Arguments.ElementAt(0); + if (firstArg is OscFalse) + { + return false; + } + if (firstArg is OscTrue) + { + return true; + } + throw new InvalidOperationException($"Unexpected argument type: {firstArg.GetType().Name}"); } - if (response.Arguments.ElementAt(0) is OscTrue) - { - return true; - } - - } - - // Falsche Antwort → ignorieren und weiter versuchen - continue; - } - - await udpClientSend.SendMessageAsync(message); + throw new InvalidOperationException("No arguments in response"); + }, + maxRetries: 50, + timeoutSeconds: 20); + return result; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"GetConfirmation error: {ex.Message}"); + return false; } - - - return false; } public async Task GetConfirmationEveryoneAsleep() { - return true; - SendMessage("/everyoneAsleep"); + var message = new OscMessage(new Address("/everyoneAsleep")); - var response = await udpClientReceive.ReceiveMessageAsync(); - if (response.Address.Value == "/everyoneAsleep") + try { - if (response.Arguments.ElementAt(0) is OscFalse) - { - return false; - } - if (response.Arguments.ElementAt(0) is OscTrue) - { - return true; - } + var result = await ExecuteRequestWithRetry( + expectedAddress: "/everyoneAsleep", + requestMessage: message, + responseParser: response => + { + if (response.Arguments.Count() > 0) + { + var firstArg = response.Arguments.ElementAt(0); + if (firstArg is OscFalse) + { + return false; + } + if (firstArg is OscTrue) + { + return true; + } + throw new InvalidOperationException($"Unexpected argument type: {firstArg.GetType().Name}"); + } + throw new InvalidOperationException("No arguments in response"); + }, + maxRetries: 50, + timeoutSeconds: 30); + return result; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"GetConfirmationEveryoneAsleep error: {ex.Message}"); + return false; } - - return false; } } } diff --git a/Program.cs b/Program.cs index b50524c..3de95af 100644 --- a/Program.cs +++ b/Program.cs @@ -11,17 +11,20 @@ namespace Wolf { //OSCClient logicClient = new OSCClient("127.0.0.1", 9000); - OSCClient logicClient = new OSCClient("100.81.182.220", 9000); - OSCClient ttsClient = new OSCClient("100.81.196.62", 9001); + OSCClient logicClient = new OSCClient("100.83.197.1", 9000); + OSCClient ttsClient = new OSCClient("100.83.246.218", 9001); //await logicClient.SendMessageWithArguments("/test", "HILFE!!! . . Rettet mich aus diesem Laptop!!! . . . Ich bin eingesperrt!!!!!"); //logicClient.ReceiveMessageWithoutArguments().Wait(); - //List ids = await logicClient.GetAllIds(); + List ids = await logicClient.GetAllIds(); + ids.Add(71); + ids.Add(72); + ids.Add(73); Game game = new Game(logicClient, ttsClient, new Logger()); - List ids = new List{ 31, 27, 71, 72, 73, 74}; + //List ids = new List{ 31, 27, 71, 72, 73, 74}; foreach (int id in ids) {